Full Code of cyberdelia/django-pipeline for AI

master d404c473f05c cached
137 files
247.0 KB
66.0k tokens
428 symbols
1 requests
Download .txt
Showing preview only (277K chars total). Download the full file or copy to clipboard to get everything.
Repository: cyberdelia/django-pipeline
Branch: master
Commit: d404c473f05c
Files: 137
Total size: 247.0 KB

Directory structure:
gitextract_vhr1bgzi/

├── .flake8
├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── release.yml
│       └── test.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .readthedocs.yaml
├── AUTHORS
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.rst
├── HISTORY.rst
├── LICENSE
├── MANIFEST.in
├── README.rst
├── docs/
│   ├── Makefile
│   ├── compilers.rst
│   ├── compressors.rst
│   ├── conf.py
│   ├── configuration.rst
│   ├── index.rst
│   ├── installation.rst
│   ├── make.bat
│   ├── signals.rst
│   ├── storages.rst
│   ├── templates.rst
│   ├── usage.rst
│   └── using.rst
├── package.json
├── pipeline/
│   ├── __init__.py
│   ├── collector.py
│   ├── compilers/
│   │   ├── __init__.py
│   │   ├── coffee.py
│   │   ├── es6.py
│   │   ├── less.py
│   │   ├── livescript.py
│   │   ├── sass.py
│   │   ├── stylus.py
│   │   └── typescript.py
│   ├── compressors/
│   │   ├── __init__.py
│   │   ├── closure.py
│   │   ├── csshtmljsminify.py
│   │   ├── cssmin.py
│   │   ├── csstidy.py
│   │   ├── jsmin.py
│   │   ├── terser.py
│   │   ├── uglifyjs.py
│   │   ├── yuglify.py
│   │   └── yui.py
│   ├── conf.py
│   ├── exceptions.py
│   ├── finders.py
│   ├── forms.py
│   ├── glob.py
│   ├── jinja2/
│   │   ├── __init__.py
│   │   └── pipeline/
│   │       ├── css.jinja
│   │       ├── inline_js.jinja
│   │       └── js.jinja
│   ├── middleware.py
│   ├── packager.py
│   ├── signals.py
│   ├── storage.py
│   ├── templates/
│   │   └── pipeline/
│   │       ├── compile_error.html
│   │       ├── css.html
│   │       ├── css.jinja
│   │       ├── inline_js.html
│   │       ├── inline_js.jinja
│   │       ├── js.html
│   │       └── js.jinja
│   ├── templatetags/
│   │   ├── __init__.py
│   │   └── pipeline.py
│   ├── utils.py
│   └── views.py
├── pyproject.toml
├── tests/
│   ├── __init__.py
│   ├── assets/
│   │   ├── compilers/
│   │   │   ├── coffee/
│   │   │   │   ├── expected.js
│   │   │   │   └── input.coffee
│   │   │   ├── es6/
│   │   │   │   ├── expected.js
│   │   │   │   └── input.es6
│   │   │   ├── less/
│   │   │   │   ├── expected.css
│   │   │   │   └── input.less
│   │   │   ├── livescript/
│   │   │   │   ├── expected.js
│   │   │   │   └── input.ls
│   │   │   ├── scss/
│   │   │   │   ├── expected.css
│   │   │   │   └── input.scss
│   │   │   ├── stylus/
│   │   │   │   ├── expected.css
│   │   │   │   └── input.styl
│   │   │   └── typescript/
│   │   │       ├── expected.js
│   │   │       └── input.ts
│   │   ├── compressors/
│   │   │   ├── closure.js
│   │   │   ├── csshtmljsminify.css
│   │   │   ├── csshtmljsminify.js
│   │   │   ├── cssmin.css
│   │   │   ├── csstidy.css
│   │   │   ├── jsmin.js
│   │   │   ├── slimit.js
│   │   │   ├── terser.js
│   │   │   ├── uglifyjs.js
│   │   │   ├── yuglify.css
│   │   │   ├── yuglify.js
│   │   │   ├── yui.css
│   │   │   └── yui.js
│   │   ├── css/
│   │   │   ├── first.css
│   │   │   ├── nested/
│   │   │   │   └── nested.css
│   │   │   ├── second.css
│   │   │   ├── sourcemap.css
│   │   │   ├── unicode.css
│   │   │   └── urls.css
│   │   ├── js/
│   │   │   ├── application.js
│   │   │   ├── dummy.coffee
│   │   │   ├── first.js
│   │   │   ├── second.js
│   │   │   └── sourcemap.js
│   │   └── templates/
│   │       ├── photo/
│   │       │   ├── detail.jst
│   │       │   └── list.jst
│   │       └── video/
│   │           └── detail.jst
│   ├── models.py
│   ├── settings.py
│   ├── templates/
│   │   ├── empty.html
│   │   └── index.html
│   ├── tests/
│   │   ├── __init__.py
│   │   ├── models.py
│   │   ├── test_collector.py
│   │   ├── test_compiler.py
│   │   ├── test_compressor.py
│   │   ├── test_conf.py
│   │   ├── test_forms.py
│   │   ├── test_glob.py
│   │   ├── test_middleware.py
│   │   ├── test_packager.py
│   │   ├── test_storage.py
│   │   ├── test_template.py
│   │   ├── test_utils.py
│   │   └── test_views.py
│   ├── urls.py
│   ├── utils.py
│   └── views.py
└── tox.ini

================================================
FILE CONTENTS
================================================

================================================
FILE: .flake8
================================================
[flake8]
max-line-length = 88
exclude =
    .tox
    node_modules
    *env/


================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "monthly"


================================================
FILE: .github/workflows/release.yml
================================================
name: Release

on:
  push:
    tags:
    - '*'

jobs:
  build:
    if: github.repository == 'jazzband/django-pipeline'
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.9'

      - name: Install dependencies
        run: |
          python -m pip install -U pip
          python -m pip install -U twine build setuptools-scm

      - name: Build package
        run: |
          python -m setuptools_scm
          python -m build
          twine check --strict dist/*

      - name: Upload packages to Jazzband
        if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          user: jazzband
          password: ${{ secrets.JAZZBAND_RELEASE_KEY }}
          repository_url: https://jazzband.co/projects/django-pipeline/upload


================================================
FILE: .github/workflows/test.yml
================================================
name: Test

on: [push, pull_request]

jobs:
  build:
    name: build (Python ${{ matrix.python-version }}, Django ${{ matrix.django-version }})
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', 'pypy-3.10']
        django-version: ['4.1', '4.2', '5.0', '5.1', '5.2', 'main']
        exclude:
          - python-version: '3.9'
            django-version: '5.0'
          - python-version: '3.9'
            django-version: '5.1'
          - python-version: '3.9'
            django-version: '5.2'
          - python-version: '3.9'
            django-version: 'main'
          - python-version: 'pypy-3.10'
            django-version: '4.1'
          - python-version: 'pypy-3.10'
            django-version: '4.2'
          - python-version: 'pypy-3.10'
            django-version: '5.0'
          - python-version: 'pypy-3.10'
            django-version: '5.1'
          - python-version: 'pypy-3.10'
            django-version: '5.2'
          - python-version: 'pypy-3.10'
            django-version: 'main'
          - python-version: '3.12'
            django-version: '4.1'
          - python-version: '3.13'
            django-version: '4.1'
          - python-version: '3.13'
            django-version: '4.2'
          - python-version: '3.13'
            django-version: '5.0'
          - python-version: '3.13'
            django-version: '5.1'

    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-java@v4
      with:
        distribution: 'temurin'
        java-version: '21'

    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python-version }}

    - name: Set up Node
      uses: actions/setup-node@v4
      with:
        node-version: '16'

    - name: Install Node dependencies
      run: npm install

    - name: Get pip cache dir
      id: pip-cache
      run: echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT

    - name: Cache
      uses: actions/cache@v4
      with:
        path: ${{ steps.pip-cache.outputs.dir }}
        key:
          ${{ matrix.python-version }}-v1-${{ hashFiles('**/pyproject.toml') }}-${{ hashFiles('**/tox.ini') }}
        restore-keys: |
          ${{ matrix.python-version }}-v1-

    - name: Install Python dependencies
      run: |
        python -m pip install --upgrade pip
        python -m pip install --upgrade tox tox-gh-actions

    - name: Tox tests
      run: |
        tox -v
      env:
        DJANGO: ${{ matrix.django-version }}

    - name: Upload coverage
      uses: codecov/codecov-action@v5
      with:
        name: Python ${{ matrix.python-version }}

  ruff:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - run: pip install --user ruff==0.12.5
    - run: ruff check . --extend-select=C4,C9,I,PLC,PLE,PLR,U --ignore=C414,I001,PLR0913,UP007,UP032 --target-version=py39


================================================
FILE: .gitignore
================================================
.AppleDouble
*.pyc
:2e_*
*.tmproj
.*.swp
*.swo
build
dist
MANIFEST
docs/_build/
*.egg-info
.coverage
coverage/
coverage.xml
tests/static/
tests/assets/js/dummy.js
tests/node_modules/
.tox/
.DS_Store
.idea
.venv
.vscode
.project
.pydevproject
.ropeproject
__pycache__
npm-debug.log
tests/npm-cache
django-pipeline-*/
.tags
node_modules/
package-lock.json


================================================
FILE: .pre-commit-config.yaml
================================================
repos:
  - repo: https://github.com/psf/black
    rev: 24.8.0
    hooks:
      - id: black

  - repo: https://github.com/PyCQA/isort
    rev: 5.13.2
    hooks:
      - id: isort

  - repo: https://github.com/PyCQA/flake8
    rev: 7.1.1
    hooks:
      - id: flake8

  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v5.0.0
    hooks:
      - id: check-merge-conflict
      - id: check-yaml

ci:
  autoupdate_schedule: quarterly


================================================
FILE: .readthedocs.yaml
================================================
# .readthedocs.yaml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details

version: 2

build:
  os: ubuntu-22.04
  tools:
    python: "3.10"

sphinx:
  configuration: docs/conf.py


================================================
FILE: AUTHORS
================================================
Pipeline is a fork of django-compress which was originally created by
Andreas Pelme <andreas@pelme.se> in 2008.

These people have provided bug fixes, new features, improved the documentation
or just made Pipeline more awesome.

 * Adam Charnock <acharnock@gmail.com>
 * Alan Lu <gotoalanlu@gmail.com>
 * Aleksey Porfirov <lexqt@yandex.ru>
 * Alex Gavrișco <alexandr@gavrisco.com>
 * Alexander Artemenko <svetlyak40wt>
 * Alexander Pugachev <alexander.pugachev@gmail.com>
 * Alexis Svinartchouk <zvin@free.fr>
 * Allard Stijnman <a.g.stijnman@gmail.com>
 * Alvin Mites <alvin@mitesdesign.com>
 * Andreas Cederström <andreas@klydd.se>
 * Andrew Choi <andrew@choi.com>
 * Andy Kish <agkish@gmail.com>
 * Ara Anjargolian <ara818@gmail.com>
 * Arnar Yngvason <arnar@hvitahusid.is>
 * Austin Pua <pua.austin.anderson@gmail.com>
 * Axel Haustant <noirbizarre@gmail.com>
 * Balazs Kossovics <balazs.kossovics@e-loue.com>
 * Ben Spaulding <ben@spaulding.im>
 * Ben Vinegar <ben@benv.ca>
 * Brad Pitcher <bradpitcher@gmail.com>
 * Brant Young <brant.young@gmail.com>
 * Brawaga <brawaga@gmail.com>
 * Brian Montgomery <brianm@appliedsec.com>
 * Bryan Chow <bryan@fullfactor.com>
 * Buky <buky.dev@outlook.com>
 * Caio Ariede <caio.ariede@gmail.com>
 * Camilo Nova <camilo.nova@gmail.com>
 * Carl Meyer <carl@oddbird.net>
 * Casey Greene <csgreene@princeton.edu>
 * Chad Miller <chad.miller@canonical.com>
 * Chris Applegate <chris.applegate@wearesocial.net>
 * Chris Reeves <hello@chris.reeves.io>
 * Christian Hammond <chipx86@chipx86.com>
 * Christofer Bertonha <christoferbertonha@gmail.com>
 * Christopher Dilorenzo <dilorenzo.christopher@gmail.com>
 * Collin Stedman <cstedman@princeton.edu>
 * Corey Farwell <coreyf@rwell.org>
 * Corrado Primier <cp@corradoprimier.it>
 * Danielle Madeley <danielle@madeley.id.au>
 * David Charbonnier <d.charbonnier@oxys.net>
 * David Cramer <dcramer@gmail.com>
 * David Hughes <d@vidhughes.com>
 * David Trowbridge <trowbrds@gmail.com>
 * Denis V Seleznyov <code@xy2.ru>
 * DJ Sharkey <dj@newscred.com>
 * Edwin Lunando <edwinlunando@gmail.com>
 * Eric Hamiter <ehamiter@gmail.com>
 * Evan Myller <eMyller@7ws.co>
 * Fabian Büchler <fabian.buechler@gmail.com>
 * Feanil Patel <feanil@edx.org>
 * Felix Last <mail@felixlast.de>
 * Florent Messa <florent.messa@gmail.com>
 * Frankie Dintino <fdintino@theatlantic.com>
 * Hannes Ljungberg <hannes.ljungberg@gmail.com>
 * Idan Zalzberg <idanzalz@gmail.com>
 * Jacob Haslehurst <jacob@haslehurst.net>
 * James Keys <skolsuper@gmail.com>
 * Jannis Leidel <jannis@leidel.info>
 * Jared Scott <jscott@convertro.com>
 * Jaromir Fojtu <jaromir.fojtu@gmail.com>
 * Jeff Held <jheld135@gmail.com>
 * John Whitlock <John-Whitlock@ieee.org> (@jwhitlock)
 * Jon Dufresne <jon.dufresne@gmail.com>
 * Josh Braegger <rckclmbr@gmail.com>
 * Joshua Kehn <josh@kehn.us>
 * Julien Hartmann <julien@etherdream.org>
 * Kevin Fox <kevin_fox@me.com> (@KFoxder)
 * Kristian Glass <git@doismellburning.co.uk>
 * Kyle MacFarlane <kyle@deletethetrees.com>
 * Leonardo Orozco <leonardoorozcop@gmail.com>
 * Luke Yu-Po Chen <nuemail@gmail.com>
 * Mark Sandstrom <mark@deliciouslynerdy.com>
 * Matt Dennewitz <mattdennewitz@gmail.com>
 * Matthieu Gallet <matthieu.gallet@19pouces.net>
 * Max Klymyshyn <klymyshyn@gmail.com>
 * Melvin Laplanche <melvin.laplanche+dev@gmail.com>
 * Michael Weibel <michael.weibel@gmail.com>
 * Michał Górny <mgorny@gentoo.org>
 * Miguel Araujo Perez <miguel.araujo.perez@gmail.com>
 * Mike Gilbert <floppym@gentoo.org>
 * Miroslav Shubernetskiy <miroslav@miki725.com>
 * Natal Ngetal <natal.ngetal@novapost.fr>
 * Nathan Cox <akujin@akujin.com>
 * Nathan Shafer <nate@torzo.com>
 * Patrick Altman <paltman@gmail.com>
 * Peter Baumgartner <pete@lincolnloop.com>
 * Peyman Salehi <slh.peyman@gmail.com>
 * Philipp Wollermann <philipp.wollermann@gmail.com>
 * Pierre Drescher <pierre.drescher@gmail.com>
 * Rajiv Bose <nerd.bose@gmail.com>
 * Rami Chowdhury <rami.chowdhury@gmail.com>
 * Remco Wendt <remco@maykinmedia.nl>
 * Remy Sanchez <remy.sanchez@hyperthese.net>
 * Sam Thomson <sammthomson@gmail.com>
 * Sander Smits <jhmsmits@gmail.com>
 * Sander Steffann <sander@steffann.nl>
 * Sassan Haradji (@sassanh)
 * Sayed Raianul Kabir <raian@newscred.com>
 * Simon Lydell <simon.lydell@gmail.com>
 * Sirex <sirexas@gmail.com>
 * Sławek Ehlert <slafs@op.pl>
 * Stefano Brentegani <sbrentegani@gmail.com>
 * Stephan Wienczny <stephan@wienczny.de>
 * Steven Cummings <estebistec@gmail.com>
 * Tadas Dailyda <tadas@dailyda.com>
 * Teo Klestrup Röijezon <teo@nullable.se>
 * Thomas Parslow <tom@almostobsolete.net>
 * Tiago Espinha <tiago@espinha.nl>
 * Timothée Peignier <timothee.peignier@tryphon.org>
 * Tom Yam <tomyam1@gmail.com>
 * Tomek Paczkowski <tomek@hauru.eu>
 * Trey Smith <trey.smith@nasa.gov>
 * Vadym S. Khondar (@vskh)
 * Venelin Stoykov <venelin@magicsolutions.bg>
 * Victor Shnayder <victor@mitx.mit.edu>
 * Wictor Olseryd <wictor@olseryd.se>
 * Wismill
 * Zachary Kazanski <kazanski.zachary@gmail.com>
 * Zenobius Jiricek <zenobius.jiricek@gmail.com>
 * Zeus Kronion
 

================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Code of Conduct

As contributors and maintainers of the Jazzband projects, and in the interest of
fostering an open and welcoming community, we pledge to respect all people who
contribute through reporting issues, posting feature requests, updating documentation,
submitting pull requests or patches, and other activities.

We are committed to making participation in the Jazzband a harassment-free experience
for everyone, regardless of the level of experience, gender, gender identity and
expression, sexual orientation, disability, personal appearance, body size, race,
ethnicity, age, religion, or nationality.

Examples of unacceptable behavior by participants include:

- The use of sexualized language or imagery
- Personal attacks
- Trolling or insulting/derogatory comments
- Public or private harassment
- Publishing other's private information, such as physical or electronic addresses,
  without explicit permission
- Other unethical or unprofessional conduct

The Jazzband roadies have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are not
aligned to this Code of Conduct, or to ban temporarily or permanently any contributor
for other behaviors that they deem inappropriate, threatening, offensive, or harmful.

By adopting this Code of Conduct, the roadies commit themselves to fairly and
consistently applying these principles to every aspect of managing the jazzband
projects. Roadies who do not follow or enforce the Code of Conduct may be permanently
removed from the Jazzband roadies.

This code of conduct applies both within project spaces and in public spaces when an
individual is representing the project or its community.

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
contacting the roadies at `roadies@jazzband.co`. All complaints will be reviewed and
investigated and will result in a response that is deemed necessary and appropriate to
the circumstances. Roadies are obligated to maintain confidentiality with regard to the
reporter of an incident.

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version
1.3.0, available at [https://contributor-covenant.org/version/1/3/0/][version]

[homepage]: https://contributor-covenant.org
[version]: https://contributor-covenant.org/version/1/3/0/


================================================
FILE: CONTRIBUTING.rst
================================================
.. image:: https://jazzband.co/static/img/jazzband.svg
   :target: https://jazzband.co/
   :alt: Jazzband

This is a `Jazzband <https://jazzband.co>`_ project. By contributing you agree to abide by the `Contributor Code of Conduct <https://jazzband.co/docs/conduct>`_ and follow the `guidelines <https://jazzband.co/docs/guidelines>`_.

Contribute
==========

#. Check for open issues or open a fresh issue to start a discussion around a
   feature idea or a bug. There is a **contribute!** tag for issues that should be
   ideal for people who are not very familiar with the codebase yet.
#. Fork the repository on Github to start making your changes on a topic branch.
#. Write a test which shows that the bug was fixed or that the feature works as expected.
#. Send a pull request and bug the maintainer until it gets merged and published.
   Make sure to add yourself to *AUTHORS*.

Otherwise, if you simply wants to suggest a feature or report a bug, create an issue :
https://github.com/jazzband/django-pipeline/issues


Running tests
=============

We use tox to run the test suite on different versions locally (and GitHub Actions
to automate the check for PRs).

To tun the test suite locally, please make sure your python environment has
tox and django installed::

    python3.7 -m pip install tox

Since we use a number of node.js tools, one should first install the node
dependencies. We recommend using [nvm](https://github.com/nvm-sh/nvm#installation-and-update) , tl;dr::

    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.2/install.sh | bash
    nvm install node
    nvm use node

And then simply execute tox to run the whole test matrix::

    tox


================================================
FILE: HISTORY.rst
================================================
.. :changelog:

History
=======

4.1.0
=====
* Add support for Python 3.13
* Add support for Django 5.2

4.0.0
=====
* Drop support for Python 3.8
* Confirm support for Django 5.1 and drop support for Django 3.2
* Use pyproject.toml

3.1.0
=====

* Replace deprecated .warn method with .warning
* Update sourcemap paths when concatenating source files
* Ensure correct compiler error styling and strip ANSI escape sequences

3.0.0
=====

* Use Pypy 3.10
* Drop support for Python 3.7
* Drop support for Django 2
* Add Python 3.12 support
* Add Django 4.2 support
* Add Django 5.0 support

2.1.0
=====

* Update README.rst and add Pipeline overview image.
* Add TypeScript compiler support.
* Drop support for ``manifesto`` package.
* Add support for Python 3.11 and Django 4.1


2.0.9
=====

* Fixed some typos in the docs.
* Fixed string type of errors reported from compilers and compressors.
* Updated github actions matrix for host and django support.
* Updated github actions configuration to use modern versions of third-party
  actions.
* Improved the packager to copy files to (S3) storage if it does not exist
  (#502).


2.0.8
=====

* Added **Django 4.0** compatibility. Thanks to @kevinmarsh (#760)
* Add tests for **Django 4.0**,  **Python 3.9** and **Python 3.10**.
  Thank to @kevinmarsh (#739)
* Introduce CODE_OF_CONDUCT.md for the project. Thank to @hugovk (#758)
* Add precision in the documentation for PipelineCachedStorage.
  Thank to @gatsinski (#739)
* Drop support for slimit compressor (#765) due to package not released
  an official version for Python 3 and not any new package release from 2013.
* Edit github actions matrix: django 3.2.9 support python 3.10, remove
  python 4.0 (doesn't exist) and exclude pypy-3.8 for django-main.
* Add .pre-commit-config.yaml. Thanks to @hugovk (#762)
* Update package.json due to CoffeeScript on NPM has moved to "coffeescript"
* Update setup.py with Django 4.0 and Python 3.10

2.0.7
=====

* Added **Django 3.2** compatibility (Thanks to @jramnai in #751)

2.0.6
======

* Added terser (JS compressor for ES5 and ES6) (Thanks to @felix-last in #696)
* Moved tests to GitHub Actions: https://github.com/jazzband/django-pipeline/actions (#738)
* Fixed deprecation warnings from Django (Thanks to @edelvalle in #731)

2.0.5
======

* Adding **Django 3.1** compatibility.
* CachedStaticFilesStorage is removed from Django. Add a check
  of the current version to prevent error while importing. Thank to @vmsp
* Context in django.template.base is removed from Django and
  not used anymore in django-pipeline.
* Fixing widgets tests of django-pipeline due to Media.render_js change in
  Django. More information in Django ticket #31892

2.0.4
======

* Adding **css-html-js-minify** support to compress JS and CSS.
* Update compressors documentation with css-html-js-minify.
* Create tests for css-html-js-minify compressor.
* Optimization by grouping the tests yuglify compressor.

2.0.3
======

* Remove futures from pipeline **setup.py** requirements.

2.0.2
=====

* Fix Middleware to properly decode HTML. Thank to @gatsinski
* Keep mimetypes as str. Thank to @benspaulding
* Based on #642 add 'NonPackagingPipelineManifestStorage' and update
  the documentation: **storages.rst**. Thank to @kronion

2.0.1
=====

* Add subclass of ManifestStaticFilesStorage. Thank to @jhpinson
* Change the documentation to use PipelineManifestStorage in configuration
  instead of PipelineCachedStorage now deprecated.
* Change import MutableMapping from collections.abc. Thank to @colons

2.0.0
=====

* **Definitely drop the support of Python 2**.
* Drop support for Python 3.5 (not compatible with PEP 498).
* Remove 'decorator.py' how was used for backward compatibility
  between python 2 and 3 for metaclass inheritance on PipelineFormMedia.
* Replace 'format' by 'fstring' (PEP 498: Literal String Interpolation).
* Remove of old imports form 'django.utils.six' and these fixes (1.7.0).
* Remove tests of uncovered versions of Python and Django.
* Replace tests for Pypy by Pypy3.
* Explicitly specify when files are read / write in binary mode.
* Set opening files for tests to deal with universal newlines.
* Upgrade documentation version to 2.0 to follow the project version.

1.7.0
=====

* Release the last major version of django-pipeline working on Python 2.
* Thank you for all the modifications made since version 1.6.14, which we cannot quote.
* Apply an optimization to save time during development. Thank to @blankser
* Edit setup.py to follow the recommendation of the documentation. Thank to @shaneikennedy
* Add tests for Django 3.0 and Python 3.8
* Add alternatives imports for django.utils.six, who has been removed in Django 3.0

1.6.14
======

* Fix packaging issues.

1.6.13
======

* Fix forward-slashed paths on Windows. Thanks to @etiago
* Fix CSS URL detector to match quotes correctly. Thanks to @vskh
* Add a compiler_options dict to compile, to allow passing options to custom
  compilers. Thanks to @sassanh
* Verify support for Django 1.11. Thanks to @jwhitlock

1.6.12
======

* Supports Django 1.11
* Fix a bug with os.rename on windows. Thanks to @wismill
* Fix to view compile error if happens. Thanks to @brawaga
* Add support for Pipeline CSS/JS packages in forms and widgets. Thanks to @chipx86

1.6.11
======

* Fix performance regression. Thanks to Christian Hammond.

1.6.10
======

* Added Django 1.10 compatiblity issues. Thanks to Austin Pua and Silvan Spross.
* Documentation improvements. Thanks to Chris Streeter.

1.6.9
=====

* Various build improvements.
* Improved setup.py classifiers. Thanks to Sobolev Nikita.
* Documentation improvements. Thanks to Adam Chainz.

1.6.8
=====

* Made templatetags easier to subclass for special rendering behavior. Thanks
  to Christian Hammond.
* Updated the link to readthedocs. Thanks to Corey Farwell.
* Fixed some log messages to correctly refer to the new PIPELINE settings
  tructure. Thanks to Alvin Mites.
* Changed file outdated checks to use os.path methods directly, avoiding
  potential SuspiciousFileOperation errors which could appear with some django
  storage configurations.

1.6.7
=====

* Add a view for collecting static files before serving them. This behaves like
  django's built-in ``static`` view and allows running the collector for
  images, fonts, and other static files that do not need to be compiled. Thanks
  to Christian Hammond.
* Update documentation for the ES6Compiler to clarify filename requirements.
  Thanks to Nathan Cox.
* Add error output for compiler errors within the browser. This provides for a
  much better experience when compiling files from the devserver. Thanks to
  Christian Hammond.
* Make unit tests run against Django 1.6 and 1.7. Thanks to Sławek Ehlert.

1.6.6
=====

* Fix filtering-out of files which require a finder to locate.
* Allow compilers to override the output path.
* Fix error reporting when a compiler fails to execute.
* Fix IOErrors when running collectstatic with some nodejs-based compilers and
  compressors. Thanks to Frankie Dintino.
* Fix compatibility of unit tests when running on Windows. Thanks to Frankie
  Dintino.
* Add unit tests for compilers and compressors. Thanks to Frankie Dintino.

1.6.5
=====

* Fix Django < 1.8 compatibility. Thanks to David Trowbridge.
* Allow to disable collector during development. Thanks to Leonardo Orozco.

1.6.4
=====

* Fix compressor subprocess calls.

1.6.3
=====

* Fix compressor command flattening.

1.6.2
=====

* Remove subprocess32 usage since it breaks universal support.

1.6.1
=====

* Fix path quoting issues. Thanks to Chad Miller.
* Use subprocess32 package when possible.
* Documentation fixes. Thanks to Sławek Ehlert and Jannis Leidel.

1.6.0
=====

* Add full support for Django 1.9.
* Drop support for Django 1.7.
* Drop support for Python 2.6.
* **BACKWARD INCOMPATIBLE** : Change configuration settings.


================================================
FILE: LICENSE
================================================
Copyright (©) 2008 Andreas Pelme <andreas@pelme.se>
Copyright (©) 2011-2018 Timothée Peignier <timothee.peignier@tryphon.org>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

================================================
FILE: MANIFEST.in
================================================
recursive-include pipeline/templates *.html *.jinja
recursive-include pipeline/jinja2 *.html *.jinja
include AUTHORS LICENSE README.rst HISTORY.rst CONTRIBUTING.rst
recursive-include tests *
recursive-exclude tests *.pyc *.pyo
recursive-exclude tests/node_modules *
recursive-exclude tests/npm-cache *
recursive-exclude tests/npm *
include docs/Makefile docs/make.bat docs/conf.py
recursive-include docs *.rst
exclude package.json requirements.txt tox.ini


================================================
FILE: README.rst
================================================
Pipeline
========

.. image:: https://jazzband.co/static/img/badge.svg
    :alt: Jazzband
    :target: https://jazzband.co/

.. image:: https://github.com/jazzband/django-pipeline/workflows/Test/badge.svg
   :target: https://github.com/jazzband/django-pipeline/actions
   :alt: GitHub Actions

.. image:: https://codecov.io/gh/jazzband/django-pipeline/branch/master/graph/badge.svg
   :target: https://codecov.io/gh/jazzband/django-pipeline
   :alt: Coverage

.. image:: https://readthedocs.org/projects/django-pipeline/badge/?version=latest
    :alt: Documentation Status
    :target: https://django-pipeline.readthedocs.io/en/latest/?badge=latest


Pipeline is an asset packaging library for Django, providing both CSS and
JavaScript concatenation and compression, built-in JavaScript template support,
and optional data-URI image and font embedding.

.. image:: https://github.com/jazzband/django-pipeline/raw/master/img/django-pipeline.svg
   :alt: Django Pipeline Overview


Installation
------------

To install it, simply:

.. code-block:: bash

    pip install django-pipeline


Quickstart
----------

Pipeline compiles and compress your assets files from
``STATICFILES_DIRS`` to your ``STATIC_ROOT`` when you run Django's
``collectstatic`` command.

These simple steps add Pipeline to your project to compile multiple ``.js`` and
``.css`` file into one and compress them.

Add Pipeline to your installed apps:

.. code-block:: python

    # settings.py
    INSTALLED_APPS = [
        ...
        'pipeline',
    ]


Use Pipeline specified classes for ``STATICFILES_FINDERS`` and ``STATICFILES_STORAGE``:

.. code-block:: python

    STATICFILES_STORAGE = 'pipeline.storage.PipelineManifestStorage'

    STATICFILES_FINDERS = (
        'django.contrib.staticfiles.finders.FileSystemFinder',
        'django.contrib.staticfiles.finders.AppDirectoriesFinder',
        'pipeline.finders.PipelineFinder',
    )


Configure Pipeline:

.. code-block:: python

    # The folowing config merges CSS files(main.css, normalize.css)
    # and JavaScript files(app.js, script.js) and compress them using
    # `yuglify` into `css/styles.css` and `js/main.js`
    # NOTE: Pipeline only works when DEBUG is False
    PIPELINE = {
        'STYLESHEETS': {
            'css_files': {
                'source_filenames': (
                    'css/main.css',
                    'css/normalize.css',
                ),
                'output_filename': 'css/styles.css',
                'extra_context': {
                    'media': 'screen,projection',
                },
            },
        },
        'JAVASCRIPT': {
            'js_files': {
                'source_filenames': (
                    'js/app.js',
                    'js/script.js',
                ),
                'output_filename': 'js/main.js',
            }
        }
    }


Then, you have to install compilers and compressors binary manually.

For example, you can install them using `NPM <https://www.npmjs.com/>`_
and address them from ``node_modules`` directory in your project path:

.. code-block:: python

    PIPELINE.update({
        'YUGLIFY_BINARY': path.join(BASE_DIR, 'node_modules/.bin/yuglify'),
    })
    # For a list of all supported compilers and compressors see documentation


Load static files in your template:

.. code-block::

    {% load pipeline %}
    {% stylesheet 'css_files' %}
    {% javascript 'js_files' %}


Documentation
-------------

For documentation, usage, and examples, see:
https://django-pipeline.readthedocs.io


Issues
------
You can report bugs and discuss features on the `issues page <https://github.com/jazzband/django-pipeline/issues>`_.


Changelog
---------

See `HISTORY.rst <https://github.com/jazzband/django-pipeline/blob/master/HISTORY.rst>`_.


================================================
FILE: docs/Makefile
================================================
# Makefile for Sphinx documentation
#

# You can set these variables from the command line.
SPHINXOPTS    =
SPHINXBUILD   = sphinx-build
PAPER         =
BUILDDIR      = _build

# Internal variables.
PAPEROPT_a4     = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .

.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest

help:
	@echo "Please use \`make <target>' where <target> is one of"
	@echo "  html       to make standalone HTML files"
	@echo "  dirhtml    to make HTML files named index.html in directories"
	@echo "  singlehtml to make a single large HTML file"
	@echo "  pickle     to make pickle files"
	@echo "  json       to make JSON files"
	@echo "  htmlhelp   to make HTML files and a HTML help project"
	@echo "  qthelp     to make HTML files and a qthelp project"
	@echo "  devhelp    to make HTML files and a Devhelp project"
	@echo "  epub       to make an epub"
	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
	@echo "  text       to make text files"
	@echo "  man        to make manual pages"
	@echo "  changes    to make an overview of all changed/added/deprecated items"
	@echo "  linkcheck  to check all external links for integrity"
	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"

clean:
	-rm -rf $(BUILDDIR)/*

html:
	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
	@echo
	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."

dirhtml:
	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
	@echo
	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."

singlehtml:
	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
	@echo
	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."

pickle:
	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
	@echo
	@echo "Build finished; now you can process the pickle files."

json:
	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
	@echo
	@echo "Build finished; now you can process the JSON files."

htmlhelp:
	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
	@echo
	@echo "Build finished; now you can run HTML Help Workshop with the" \
	      ".hhp project file in $(BUILDDIR)/htmlhelp."

qthelp:
	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/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-pipeline.qhcp"
	@echo "To view the help file:"
	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-pipeline.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-pipeline"
	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-pipeline"
	@echo "# devhelp"

epub:
	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
	@echo
	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."

latex:
	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
	@echo
	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
	@echo "Run \`make' in that directory to run these through (pdf)latex" \
	      "(use \`make latexpdf' here to do that automatically)."

latexpdf:
	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
	@echo "Running LaTeX files through pdflatex..."
	make -C $(BUILDDIR)/latex all-pdf
	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."

text:
	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
	@echo
	@echo "Build finished. The text files are in $(BUILDDIR)/text."

man:
	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
	@echo
	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."

changes:
	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
	@echo
	@echo "The overview file is in $(BUILDDIR)/changes."

linkcheck:
	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
	@echo
	@echo "Link check complete; look for any errors in the above output " \
	      "or in $(BUILDDIR)/linkcheck/output.txt."

doctest:
	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
	@echo "Testing of doctests in the sources finished, look at the " \
	      "results in $(BUILDDIR)/doctest/output.txt."


================================================
FILE: docs/compilers.rst
================================================
.. _ref-compilers:

=========
Compilers
=========

TypeScript compiler
======================

The TypeScript compiler uses `TypeScript <https://www.typescriptlang.org/>`_
to compile your TypeScript code to JavaScript.

To use it add this to your ``PIPELINE['COMPILERS']`` ::

  PIPELINE['COMPILERS'] = (
    'pipeline.compilers.typescript.TypeScriptCompiler',
  )

``TYPE_SCRIPT_BINARY``
---------------------------------

  Command line to execute for TypeScript program.
  You will most likely change this to the location of ``tsc`` on your system.

  Defaults to ``'/usr/bin/env tsc'``.

``TYPE_SCRIPT_ARGUMENTS``
------------------------------------

  Additional arguments to use when ``tsc`` is called.

  Defaults to ``''``.

Coffee Script compiler
======================

The Coffee Script compiler uses `Coffee Script <http://jashkenas.github.com/coffeescript/>`_
to compile your javascript.

To use it add this to your ``PIPELINE['COMPILERS']`` ::

  PIPELINE['COMPILERS'] = (
    'pipeline.compilers.coffee.CoffeeScriptCompiler',
  )

``COFFEE_SCRIPT_BINARY``
---------------------------------

  Command line to execute for coffee program.
  You will most likely change this to the location of coffee on your system.

  Defaults to ``'/usr/bin/env coffee'``.

``COFFEE_SCRIPT_ARGUMENTS``
------------------------------------

  Additional arguments to use when coffee is called.

  Defaults to ``''``.

Live Script compiler
======================

The LiveScript compiler uses `LiveScript <https://github.com/gkz/LiveScript>`_
to compile your javascript.

To use it add this to your ``PIPELINE['COMPILERS']`` ::

  PIPELINE['COMPILERS'] = (
    'pipeline.compilers.livescript.LiveScriptCompiler',
  )

``LIVE_SCRIPT_BINARY``
---------------------------------

  Command line to execute for LiveScript program.
  You will most likely change this to the location of lsc on your system.

  Defaults to ``'/usr/bin/env lsc'``.

``LIVE_SCRIPT_ARGUMENTS``
------------------------------------

  Additional arguments to use when lsc is called.

  Defaults to ``''``.

LESS compiler
=============

The LESS compiler uses `LESS <http://lesscss.org/>`_
to compile your stylesheets.

To use it add this to your ``PIPELINE['COMPILERS']`` ::

  PIPELINE['COMPILERS'] = (
    'pipeline.compilers.less.LessCompiler',
  )

``LESS_BINARY``
------------------------

  Command line to execute for lessc program.
  You will most likely change this to the location of lessc on your system.

  Defaults to ``'/usr/bin/env lessc'``.

``LESS_ARGUMENTS``
---------------------------

  Additional arguments to use when lessc is called.

  Defaults to ``''``.

SASS compiler
=============

The SASS compiler uses `SASS <http://sass-lang.com/>`_
to compile your stylesheets.

To use it add this to your ``PIPELINE['COMPILERS']`` ::

  PIPELINE['COMPILERS'] = (
    'pipeline.compilers.sass.SASSCompiler',
  )


``SASS_BINARY``
------------------------

  Command line to execute for sass program.
  You will most likely change this to the location of sass on your system.

  Defaults to ``'/usr/bin/env sass'``.

``SASS_ARGUMENTS``
---------------------------

  Additional arguments to use when sass is called.

  Defaults to ``''``.


Stylus compiler
===============

The Stylus compiler uses `Stylus <http://learnboost.github.com/stylus/>`_
to compile your stylesheets.

To use it add this to your ``PIPELINE['COMPILERS']`` ::

  PIPELINE['COMPILERS'] = (
      'pipeline.compilers.stylus.StylusCompiler',
  )


``STYLUS_BINARY``
--------------------------

  Command line to execute for stylus program.
  You will most likely change this to the location of stylus on your system.

  Defaults to ``'/usr/bin/env stylus'``.

``STYLUS_ARGUMENTS``
-----------------------------

  Additional arguments to use when stylus is called.

  Defaults to ``''``.


ES6 compiler
============

The ES6 compiler uses `Babel <https://babeljs.io>`_
to convert ES6+ code into vanilla ES5.

Note that for files to be transpiled properly they must have the file extension **.es6**

To use it add this to your ``PIPELINE['COMPILERS']`` ::

  PIPELINE['COMPILERS'] = (
      'pipeline.compilers.es6.ES6Compiler',
  )
  

``BABEL_BINARY``
--------------------------

  Command line to execute for babel program.
  You will most likely change this to the location of babel on your system.

  Defaults to ``'/usr/bin/env babel'``.

``BABEL_ARGUMENTS``
-----------------------------

  Additional arguments to use when babel is called.

  Defaults to ``''``.


Write your own compiler class
=============================

You can write your own compiler class, for example if you want to implement other types
of compilers.

To do so, you just have to create a class that inherits from ``pipeline.compilers.CompilerBase``
and implements ``match_file`` and ``compile_file`` when needed.

Finally, specify it in the tuple of compilers ``PIPELINE['COMPILERS']`` in the settings.

Example
-------

A custom compiler for an imaginary compiler called jam ::

  from pipeline.compilers import CompilerBase

  class JamCompiler(CompilerBase):
    output_extension = 'js'

    def match_file(self, filename):
      return filename.endswith('.jam')

    def compile_file(self, infile, outfile, outdated=False, force=False):
      if not outdated and not force:
        return  # No need to recompiled file
      return jam.compile(infile, outfile)


3rd Party Compilers
===================

Here is an (in)complete list of 3rd party compilers that integrate with django-pipeline

Compass (requires RubyGem)
--------------------------

:Creator:
    `Mila Labs <https://github.com/mila-labs>`_
:Description:
    Compass compiler for django-pipeline using the original Ruby gem.
:Link:
    `https://github.com/mila-labs/django-pipeline-compass-rubygem`

Compass (standalone)
--------------------

:Creator:
    `Vitaly Babiy <https://github.com/vbabiy>`_
:Description:
    django-pipeline-compass is a compiler for `django-pipeline <https://github.com/jazzband/django-pipeline>`_. Making it really easy to use scss and compass with out requiring the compass gem.
:Link:
    `https://github.com/vbabiy/django-pipeline-compass`

Libsass (standalone)
--------------------

:Creator:
    `Johanderson Mogollon <https://github.com/sonic182>`_
:Description:
    libsasscompiler is a compiler for `django-pipeline <https://github.com/jazzband/django-pipeline>`_. Making it really easy to use scss/sass with the super fast libsass library.
:Link:
    `https://github.com/sonic182/libsasscompiler`


================================================
FILE: docs/compressors.rst
================================================
.. _ref-compressors:

===========
Compressors
===========


Yuglify compressor
==================

The Yuglify compressor uses `yuglify <http://github.com/yui/yuglify>`_
for compressing javascript and stylesheets.

To use it for your stylesheets add this to your ``PIPELINE['CSS_COMPRESSOR']`` ::

  PIPELINE['CSS_COMPRESSOR'] = 'pipeline.compressors.yuglify.YuglifyCompressor'

To use it for your javascripts add this to your ``PIPELINE['JS_COMPRESSOR']`` ::

  PIPELINE['JS_COMPRESSOR'] = 'pipeline.compressors.yuglify.YuglifyCompressor'


``YUGLIFY_BINARY``
---------------------------

  Command line to execute for the Yuglify program.
  You will most likely change this to the location of yuglify on your system.

  Defaults to ``'/usr/bin/env yuglify'``.

``YUGLIFY_CSS_ARGUMENTS``
----------------------------------

  Additional arguments to use when compressing CSS.

  Defaults to ``'--terminal'``.

``YUGLIFY_JS_ARGUMENTS``
---------------------------------

  Additional arguments to use when compressing JavaScript.

  Defaults to ``'--terminal'``.


YUI Compressor compressor
=========================

The YUI compressor uses `yui-compressor <http://developer.yahoo.com/yui/compressor/>`_
for compressing javascript and stylesheets.

To use it for your stylesheets add this to your ``PIPELINE['CSS_COMPRESSOR']`` ::

  PIPELINE['CSS_COMPRESSOR'] = 'pipeline.compressors.yui.YUICompressor'

To use it for your javascripts add this to your ``PIPELINE['JS_COMPRESSOR']`` ::

  PIPELINE['JS_COMPRESSOR'] = 'pipeline.compressors.yui.YUICompressor'


``YUI_BINARY``
-----------------------

  Command line to execute for the YUI program.
  You will most likely change this to the location of yui-compressor on your system.

  Defaults to ``'/usr/bin/env yuicompressor'``.

.. warning::
  Don't point to ``yuicompressor.jar`` directly, we expect to find a executable script.


``YUI_CSS_ARGUMENTS``
------------------------------

  Additional arguments to use when compressing CSS.

  Defaults to ``''``.

``YUI_JS_ARGUMENTS``
-----------------------------

  Additional arguments to use when compressing JavaScript.

  Defaults to ``''``.


Closure Compiler compressor
===========================

The Closure compressor uses `Google Closure Compiler <http://code.google.com/closure/compiler/>`_
to compress javascripts.

To use it add this to your ``PIPELINE['JS_COMPRESSOR']`` ::

  PIPELINE['JS_COMPRESSOR'] = 'pipeline.compressors.closure.ClosureCompressor'


``CLOSURE_BINARY``
---------------------------

  Command line to execute for the Closure Compiler program.
  You will most likely change this to the location of closure on your system.

  Default to ``'/usr/bin/env closure'``

.. warning::
  Don't point to ``compiler.jar`` directly, we expect to find a executable script.


``CLOSURE_ARGUMENTS``
------------------------------

  Additional arguments to use when closure is called.

  Default to ``''``


UglifyJS compressor
===================

The UglifyJS compressor uses `UglifyJS <https://github.com/mishoo/UglifyJS2/>`_ to
compress javascripts.

To use it add this to your ``PIPELINE['JS_COMPRESSOR']`` ::

  PIPELINE['JS_COMPRESSOR'] = 'pipeline.compressors.uglifyjs.UglifyJSCompressor'


``UGLIFYJS_BINARY``
----------------------------

  Command line to execute for the UglifyJS program.
  You will most likely change this to the location of uglifyjs on your system.

  Defaults to ``'/usr/bin/env uglifyjs'``.

``UGLIFYJS_ARGUMENTS``
-------------------------------

  Additional arguments to use when uglifyjs is called.

  Default to ``''``


JSMin compressor
================

The jsmin compressor uses Douglas Crockford jsmin tool to
compress javascripts.

To use it add this to your ``PIPELINE['JS_COMPRESSOR']`` ::

  PIPELINE['JS_COMPRESSOR'] = 'pipeline.compressors.jsmin.JSMinCompressor'

Install the jsmin library with your favorite Python package manager ::

  pip install jsmin


Terser compressor
===================

`Terser <https://github.com/terser/terser>`_ is a JavaScript parser and 
mangler/compressor toolkit for ES6+. It has been designed as a successor of
``uglify-es`` and ``uglify-js``. The compressor works with ES5 and ES6 and 
regular ``.js`` file endings.

To use it add this to your ``PIPELINE['JS_COMPRESSOR']`` ::

  PIPELINE['JS_COMPRESSOR'] = 'pipeline.compressors.terser.TerserCompressor'


``TERSER_BINARY``
----------------------------

  Command line to execute for the terser program.
  You will most likely change this to the location of terser on your system.

  Defaults to ``'/usr/bin/env terser'``.

``TERSER_ARGUMENTS``
-------------------------------

  Additional arguments to use when terser is called.

  Default to ``'--compress'``


CSSTidy compressor
==================

The CSStidy compressor uses `CSStidy <http://csstidy.sourceforge.net/>`_ to compress
stylesheets.

To us it for your stylesheets add this to your ``PIPELINE['CSS_COMPRESSOR']`` ::

  PIPELINE['CSS_COMPRESSOR'] = 'pipeline.compressors.csstidy.CSSTidyCompressor'

``CSSTIDY_BINARY``
---------------------------

  Command line to execute for csstidy program.
  You will most likely change this to the location of csstidy on your system.

  Defaults to ``'/usr/bin/env csstidy'``

``CSSTIDY_ARGUMENTS``
------------------------------

  Additional arguments to use when csstidy is called.

  Default to ``'--template=highest'``


CSSMin compressor
=================

The cssmin compressor uses the `cssmin <https://github.com/jbleuzen/node-cssmin>`_
command to compress stylesheets. To use it, add this to your ``PIPELINE['CSS_COMPRESSOR']`` ::

  PIPELINE['CSS_COMPRESSOR'] = 'pipeline.compressors.cssmin.CSSMinCompressor'

``CSSMIN_BINARY``
---------------------------

  Command line to execute for cssmin program.
  You will most likely change this to the location of cssmin on your system.

  Defaults to ``'/usr/bin/env cssmin'``

``CSSMIN_ARGUMENTS``
------------------------------

  Additional arguments to use when cssmin is called.

  Default to ``''``


css-html-js-minify compressor
=============================

The css-html-js-minify is full Python compressor using `css-html-js-minify <https://github.com/ciotto/css-html-js-minify>`_
for compressing javascript and stylesheets.

To use it for your stylesheets add this to your ``PIPELINE['CSS_COMPRESSOR']`` ::

  PIPELINE['CSS_COMPRESSOR'] = 'pipeline.compressors.csshtmljsminify.CssHtmlJsMinifyCompressor'

To use it for your javascripts add this to your ``PIPELINE['JS_COMPRESSOR']`` ::

  PIPELINE['JS_COMPRESSOR'] = 'pipeline.compressors.csshtmljsminify.CssHtmlJsMinifyCompressor'

Install the css-html-js-minify library with your favorite Python package manager ::

  pip install css-html-js-minify


No-Op Compressors
=================

The No-Op compressor don't perform any operation, when used, only concatenation occurs.
This is useful for debugging faulty concatenation due to poorly written javascript and other errors.

To use it, add this to your settings ::

  PIPELINE['CSS_COMPRESSOR'] = 'pipeline.compressors.NoopCompressor'
  PIPELINE['JS_COMPRESSOR'] = 'pipeline.compressors.NoopCompressor'


Write your own compressor class
===============================

You can write your own compressor class, for example if you want to implement other types
of compressors.

To do so, you just have to create a class that inherits from ``pipeline.compressors.CompressorBase``
and implements ``compress_css`` and/or a ``compress_js`` when needed.

Finally, add it to ``PIPELINE['CSS_COMPRESSOR']`` or
``PIPELINE['JS_COMPRESSOR']`` settings (see :doc:`configuration` for more information).

Example
-------

A custom compressor for an imaginary compressor called jam ::

  from pipeline.compressors import CompressorBase

  class JamCompressor(CompressorBase):
    def compress_js(self, js):
      return jam.compress(js)

    def compress_css(self, css):
      return jam.compress(css)


Add it to your settings ::

  PIPELINE['CSS_COMPRESSOR'] = 'jam.compressors.JamCompressor'
  PIPELINE['JS_COMPRESSOR'] = 'jam.compressors.JamCompressor'


================================================
FILE: docs/conf.py
================================================
# Pipeline documentation build configuration file, created by
# sphinx-quickstart on Sat Apr 30 17:47:55 2011.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
# sys.path.insert(0, os.path.abspath('.'))
from datetime import datetime

from pipeline import __version__ as pipeline_version

# -- General configuration -----------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
# needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = []

# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]

# The suffix of source filenames.
source_suffix = ".rst"

# The encoding of source files.
# source_encoding = 'utf-8-sig'

# The master toctree document.
master_doc = "index"

# General information about the project.
project = "django-pipeline"
current_year = datetime.now().year
copyright = "2011-{}, Timothée Peignier".format(current_year)

# 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 full version, including alpha/beta/rc tags.
release = pipeline_version
# The short X.Y version.
version = ".".join(release.split(".")[:2])

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
# language = None

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
# today = ''
# Else, today_fmt is used as the format for a strftime call.
# today_fmt = '%B %d, %Y'

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ["_build"]

# The reST default role (used for this markup: `text`) to use for all documents.
# default_role = None

# If true, '()' will be appended to :func: etc. cross-reference text.
# add_function_parentheses = True

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
# add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
# show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = "sphinx"

# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []


# -- Options for HTML output ---------------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
html_theme = "default"

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
# html_theme_options = {}

# Add any paths that contain custom themes here, relative to this directory.
# html_theme_path = []

# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
# html_title = None

# A shorter title for the navigation bar.  Default is the same as html_title.
# html_short_title = None

# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
# html_logo = None

# The name of an image file (within the static path) to use as favicon of the
# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
# html_favicon = None

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
# html_static_path = ['_static']

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
# html_last_updated_fmt = '%b %d, %Y'

# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
# html_use_smartypants = True

# Custom sidebar templates, maps document names to template names.
# html_sidebars = {}

# Additional templates that should be rendered to pages, maps page names to
# template names.
# html_additional_pages = {}

# If false, no module index is generated.
# html_domain_indices = True

# If false, no index is generated.
# html_use_index = True

# If true, the index is split into individual pages for each letter.
# html_split_index = False

# If true, links to the reST sources are added to the pages.
# html_show_sourcelink = True

# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
# html_show_sphinx = True

# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
# html_show_copyright = True

# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it.  The value of this option must be the
# base URL from which the finished HTML is served.
# html_use_opensearch = ''

# This is the file name suffix for HTML files (e.g. ".xhtml").
# html_file_suffix = None

# Output file base name for HTML help builder.
htmlhelp_basename = "django-pipelinedoc"


# -- Options for LaTeX output --------------------------------------------------

# The paper size ('letter' or 'a4').
# latex_paper_size = 'letter'

# The font size ('10pt', '11pt' or '12pt').
# latex_font_size = '10pt'

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
    (
        "index",
        "django-pipeline.tex",
        "Pipeline Documentation",
        "Timothée Peignier",
        "manual",
    ),
]

# The name of an image file (relative to this directory) to place at the top of
# the title page.
# latex_logo = None

# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
# latex_use_parts = False

# If true, show page references after internal links.
# latex_show_pagerefs = False

# If true, show URL addresses after external links.
# latex_show_urls = False

# Additional stuff for the LaTeX preamble.
# latex_preamble = ''

# Documents to append as an appendix to all manuals.
# latex_appendices = []

# If false, no module index is generated.
# latex_domain_indices = True


# -- Options for manual page output --------------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    ("index", "django-pipeline", "Pipeline Documentation", ["Timothée Peignier"], 1)
]


================================================
FILE: docs/configuration.rst
================================================
.. _ref-configuration:

=============
Configuration
=============


Configuration and list of available settings for Pipeline. Pipeline settings are namespaced in a PIPELINE dictionary in your project settings, e.g.: ::

  PIPELINE = {
      'PIPELINE_ENABLED': True,
      'JAVASCRIPT': {
          'stats': {
              'source_filenames': (
                'js/jquery.js',
                'js/d3.js',
                'js/collections/*.js',
                'js/application.js',
              ),
              'output_filename': 'js/stats.js',
          }
      }
  }


Specifying files
================

You specify groups of files to be compressed in your settings. You can use glob
syntax to select multiples files.

The basic syntax for specifying CSS/JavaScript groups files is ::

  PIPELINE = {
      'STYLESHEETS': {
          'colors': {
              'source_filenames': (
                'css/core.css',
                'css/colors/*.css',
                'css/layers.css'
              ),
              'output_filename': 'css/colors.css',
              'extra_context': {
                  'media': 'screen,projection',
              },
          },
      },
      'JAVASCRIPT': {
          'stats': {
              'source_filenames': (
                'js/jquery.js',
                'js/d3.js',
                'js/collections/*.js',
                'js/application.js',
              ),
              'output_filename': 'js/stats.js',
          }
      }
  }

Group options
-------------

``source_filenames``
....................

**Required**

Is a tuple with the source files to be compressed.
The files are concatenated in the order specified in the tuple.


``output_filename``
...................

**Required**

Is the filename of the (to be) compressed file.

``variant``
...........

**Optional**

Is the variant you want to apply to your CSS. This allow you to embed images
and fonts in CSS with data-URI.
Allowed values are : ``None`` and ``datauri``.

Defaults to ``None``.

``template_name``
.................

**Optional**

Name of the template used to render ``<script>`` for js package or ``<link>`` for css package.

Defaults to ``None``.

``extra_context``
.................

**Optional**

Is a dictionary of values to add to the template context,
when generating the HTML for the HTML-tags with the templatetags.

For CSS, if you do not specify ``extra_context``/``media``, the default media in
the ``<link>`` output will be ``media="all"``.

For JS, the default templates support the ``async`` and ``defer`` tag attributes which are controlled via ``extra_context``: ::

  'extra_context': {
      'async': True,
  },

``manifest``
............

**Optional**

Indicate if you want this group to appear in your cache manifest.

Defaults to ``True``.

``compiler_options``
....................

**Optional**

A dictionary passed to compiler's ``compile_file`` method as kwargs. None of default compilers use it currently. It's to be used by custom compilers in case they need some special parameters.

Defaults to ``{}``.


Other settings
--------------

``PIPELINE_ENABLED``
....................

``True`` if assets should be compressed, ``False`` if not.

Defaults to ``not settings.DEBUG``.

``PIPELINE_COLLECTOR_ENABLED``
..............................

``True`` if assets should be collected in develop , ``False`` if not.

Defaults to ``True``

.. note::

  This only applies when ``PIPELINE_ENABLED`` is ``False``.

``SHOW_ERRORS_INLINE``
......................

``True`` if errors compiling CSS/JavaScript files should be shown inline at
the top of the browser window, or ``False`` if they should trigger exceptions
(the older behavior).

This only applies when compiling through the ``{% stylesheet %}`` or
``{% javascript %}`` template tags. It won't impact ``collectstatic``.

Defaults to ``settings.DEBUG``.

``CSS_COMPRESSOR``
..................

Compressor class to be applied to CSS files.

If empty or ``None``, CSS files won't be compressed.

Defaults to ``'pipeline.compressors.yuglify.YuglifyCompressor'``.

``JS_COMPRESSOR``
.................

Compressor class to be applied to JavaScript files.

If empty or ``None``, JavaScript files won't be compressed.

Defaults to ``'pipeline.compressors.yuglify.YuglifyCompressor'``

.. note::

  Please note that in order to use Yuglify compressor, you need to install Yuglify (see :doc:`installation` for more details).

``TEMPLATE_NAMESPACE``
......................

Object name where all of your compiled templates will be added, from within your browser.
To access them with your own JavaScript namespace, change it to the object of your choice.

Defaults to ``"window.JST"``


``TEMPLATE_EXT``
................

The extension for which Pipeline will consider the file as a Javascript template.
To use a different extension, like ``.mustache``, set this settings to ``.mustache``.

Defaults to ``".jst"``

``TEMPLATE_FUNC``
.................

JavaScript function that compiles your JavaScript templates.
Pipeline doesn't bundle a javascript template library, but the default
setting is to use the
`underscore <http://documentcloud.github.com/underscore/>`_ template function.

Defaults to ``"_.template"``

``TEMPLATE_SEPARATOR``
......................

Character chain used by Pipeline as replacement for directory separator.

Defaults to ``"_"``


``MIMETYPES``
.............

Tuple that match file extension with their corresponding mimetypes.

Defaults to ::

  (
    ('text/coffeescript', '.coffee'),
    ('text/less', '.less'),
    ('text/javascript', '.js'),
    ('text/x-sass', '.sass'),
    ('text/x-scss', '.scss')
  )

.. warning::
  If you support Internet Explorer version 8 and below, you should
  declare javascript files as ``text/javascript``.


Embedding fonts and images
==========================

You can embed fonts and images directly in your compiled css, using Data-URI in
modern browsers.

To do so, setup variant group options to the method you wish to use : ::

  'STYLESHEETS' = {
      'master': {
          'source_filenames': (
            'css/core.css',
            'css/button/*.css',
          ),
          'output_filename': 'css/master.css',
          'variant': 'datauri',
      },
  }

Images and fonts are embedded following these rules :

- If asset is under **32 kilobytes** to avoid rendering delay or not rendering
  at all in Internet Explorer 8.
- If asset path contains a directory named "**embed**".

Overriding embedding settings
-----------------------------

You can override these rules using the following settings:

``EMBED_MAX_IMAGE_SIZE``
........................

Setting that controls the maximum image size (in bytes) to embed in CSS using Data-URIs.
Internet Explorer 8 has issues with assets over 32 kilobytes.

Defaults to ``32700``

``EMBED_PATH``
..............

Setting the directory that an asset needs to be in so that it is embedded

Defaults to ``r'[/]?embed/'``


Rewriting CSS urls
==================

If the source CSS contains relative URLs (i.e. relative to current file),
those URLs will be converted to full relative path.


Wrapped javascript output
=========================

All javascript output is wrapped in an anonymous function : ::

  (function(){
    //JS output...
  })();

This safety wrapper, make it difficult to pollute the global namespace by accident and improve performance.

You can override this behavior by setting ``DISABLE_WRAPPER`` to ``True``. If you want to use your own wrapper, change
the ``JS_WRAPPER`` setting. For example: ::

  JS_WRAPPER = "(function(){stuff();%s})();"


================================================
FILE: docs/index.rst
================================================
Introduction
============

Pipeline is an asset packaging library for Django, providing
both CSS and JavaScript concatenation and compression, built-in JavaScript
template support, and optional data-URI image and font embedding.

You can report bugs and discuss features on the `issues page <https://github.com/jazzband/django-pipeline/issues>`_.

You can discuss features or ask questions on the IRC channel on freenode : `#django-pipeline <irc://irc.freenode.net/django-pipeline>`_


Table Of Contents
=================

.. toctree::
   :maxdepth: 2

   installation
   configuration
   usage
   compressors
   compilers
   templates
   storages
   signals
   using


Indices and tables
==================

* :ref:`search`


================================================
FILE: docs/installation.rst
================================================
.. _ref-installation:

============
Installation
============

1. Either check out Pipeline from GitHub_ or to pull a release off PyPI_ ::

       pip install django-pipeline


2. Add 'pipeline' to your ``INSTALLED_APPS`` ::

       INSTALLED_APPS = (
           'pipeline',
       )

3. Use a pipeline storage for ``STATICFILES_STORAGE`` ::

        STATICFILES_STORAGE = 'pipeline.storage.PipelineManifestStorage'

4. Add the ``PipelineFinder`` to ``STATICFILES_FINDERS`` ::

        STATICFILES_FINDERS = (
            'django.contrib.staticfiles.finders.FileSystemFinder',
            'django.contrib.staticfiles.finders.AppDirectoriesFinder',
            'pipeline.finders.PipelineFinder',
        )


.. note::
  You need to use ``Django>=1.11`` to be able to use this version of pipeline.

.. _GitHub: http://github.com/jazzband/django-pipeline
.. _PyPI: http://pypi.python.org/pypi/django-pipeline

Upgrading from 1.3
==================

To upgrade from pipeline 1.3, you will need to follow these steps:

1. Update templates to use the new syntax

    .. code-block:: python

        {# pipeline<1.4 #}
        {% load compressed %}
        {% compressed_js 'group' %}
        {% compressed_css 'group' %}

    .. code-block:: python

        {# pipeline>=1.4 #}
        {% load pipeline %}
        {% javascript 'group' %}
        {% stylesheet 'group' %}

2. Add the ``PipelineFinder`` to ``STATICFILES_FINDERS`` ::

        STATICFILES_FINDERS = (
            'django.contrib.staticfiles.finders.FileSystemFinder',
            'django.contrib.staticfiles.finders.AppDirectoriesFinder',
            'pipeline.finders.PipelineFinder',
        )


Upgrading from 1.5
==================

To upgrade from pipeline 1.5, you will need update all your ``PIPELINE_*``
settings and move them under the new ``PIPELINE`` setting.
See :ref:`ref-configuration`.

Recommendations
===============

Pipeline's default CSS and JS compressor is Yuglify.
Yuglify wraps UglifyJS and cssmin, applying the default YUI configurations to them.
It can be downloaded from: https://github.com/yui/yuglify/.

If you do not install yuglify, make sure to disable the compressor in your settings.


================================================
FILE: docs/make.bat
================================================
@ECHO OFF

REM Command file for Sphinx documentation

if "%SPHINXBUILD%" == "" (
	set SPHINXBUILD=sphinx-build
)
set BUILDDIR=_build
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
if NOT "%PAPER%" == "" (
	set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
)

if "%1" == "" goto help

if "%1" == "help" (
	:help
	echo.Please use `make ^<target^>` where ^<target^> is one of
	echo.  html       to make standalone HTML files
	echo.  dirhtml    to make HTML files named index.html in directories
	echo.  singlehtml to make a single large HTML file
	echo.  pickle     to make pickle files
	echo.  json       to make JSON files
	echo.  htmlhelp   to make HTML files and a HTML help project
	echo.  qthelp     to make HTML files and a qthelp project
	echo.  devhelp    to make HTML files and a Devhelp project
	echo.  epub       to make an epub
	echo.  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter
	echo.  text       to make text files
	echo.  man        to make manual pages
	echo.  changes    to make an overview over all changed/added/deprecated items
	echo.  linkcheck  to check all external links for integrity
	echo.  doctest    to run all doctests embedded in the documentation if enabled
	goto end
)

if "%1" == "clean" (
	for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
	del /q /s %BUILDDIR%\*
	goto end
)

if "%1" == "html" (
	%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished. The HTML pages are in %BUILDDIR%/html.
	goto end
)

if "%1" == "dirhtml" (
	%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
	goto end
)

if "%1" == "singlehtml" (
	%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
	goto end
)

if "%1" == "pickle" (
	%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished; now you can process the pickle files.
	goto end
)

if "%1" == "json" (
	%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished; now you can process the JSON files.
	goto end
)

if "%1" == "htmlhelp" (
	%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished; now you can run HTML Help Workshop with the ^
.hhp project file in %BUILDDIR%/htmlhelp.
	goto end
)

if "%1" == "qthelp" (
	%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %BUILDDIR%/qthelp, like this:
	echo.^> qcollectiongenerator %BUILDDIR%\qthelp\django-pipeline.qhcp
	echo.To view the help file:
	echo.^> assistant -collectionFile %BUILDDIR%\qthelp\django-pipeline.ghc
	goto end
)

if "%1" == "devhelp" (
	%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished.
	goto end
)

if "%1" == "epub" (
	%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished. The epub file is in %BUILDDIR%/epub.
	goto end
)

if "%1" == "latex" (
	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
	goto end
)

if "%1" == "text" (
	%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished. The text files are in %BUILDDIR%/text.
	goto end
)

if "%1" == "man" (
	%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished. The manual pages are in %BUILDDIR%/man.
	goto end
)

if "%1" == "changes" (
	%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
	if errorlevel 1 exit /b 1
	echo.
	echo.The overview file is in %BUILDDIR%/changes.
	goto end
)

if "%1" == "linkcheck" (
	%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
	if errorlevel 1 exit /b 1
	echo.
	echo.Link check complete; look for any errors in the above output ^
or in %BUILDDIR%/linkcheck/output.txt.
	goto end
)

if "%1" == "doctest" (
	%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
	if errorlevel 1 exit /b 1
	echo.
	echo.Testing of doctests in the sources finished, look at the ^
results in %BUILDDIR%/doctest/output.txt.
	goto end
)

:end


================================================
FILE: docs/signals.rst
================================================
.. _ref-signals:

=======
Signals
=======

List of all signals sent by pipeline.

css_compressed
--------------

**pipeline.signals.css_compressed**

	Whenever a css package is compressed, this signal is sent after the compression.

	Arguments sent with this signal :
	
		:sender:
			The ``Packager`` class that compressed the group.
		
		:package:
			The package actually compressed.


js_compressed
--------------

**pipeline.signals.js_compressed**

	Whenever a js package is compressed, this signal is sent after the compression.
	
	Arguments sent with this signal :
	
		:sender:
			The ``Packager`` class that compressed the group.
		
		:package:
			The package actually compressed.


================================================
FILE: docs/storages.rst
================================================
.. _ref-storages:

========
Storages
========


Using with staticfiles
======================

Pipeline is providing a storage for `staticfiles app <https://docs.djangoproject.com/en/dev/howto/static-files/>`_,
to use it configure ``STATICFILES_STORAGE`` like so ::

  STATICFILES_STORAGE = 'pipeline.storage.PipelineStorage'

And if you want versioning use ::

  STATICFILES_STORAGE = 'pipeline.storage.PipelineManifestStorage'

There is also non-packing storage available, that allows you to run ``collectstatic`` command
without packaging your assets. Useful for production when you don't want to run compressor or compilers ::

  STATICFILES_STORAGE = 'pipeline.storage.NonPackagingPipelineStorage'

Also available if you want versioning ::

  STATICFILES_STORAGE = 'pipeline.storage.NonPackagingPipelineManifestStorage'

If you use staticfiles with ``DEBUG = False`` (i.e. for integration tests
with `Selenium <http://docs.seleniumhq.org/>`_) you should install the finder
that allows staticfiles to locate your outputted assets : ::

  STATICFILES_FINDERS = (
      'django.contrib.staticfiles.finders.FileSystemFinder',
      'django.contrib.staticfiles.finders.AppDirectoriesFinder',
      'pipeline.finders.PipelineFinder',
  )

If you use ``PipelineCachedStorage`` you may also like the ``CachedFileFinder``,
which allows you to use integration tests with cached file URLs.

Keep in mind that ``PipelineCachedStorage`` is only available for Django versions
before 3.1.

If you want to exclude Pipelinable content from your collected static files,
you can also use Pipeline's ``FileSystemFinder`` and ``AppDirectoriesFinder``.
These finders will also exclude `unwanted` content like READMEs, tests and
examples, which are particularly useful if you're collecting content from a
tool like Bower. ::

  STATICFILES_FINDERS = (
      'pipeline.finders.FileSystemFinder',
      'pipeline.finders.AppDirectoriesFinder',
      'pipeline.finders.CachedFileFinder',
      'pipeline.finders.PipelineFinder',
  )

GZIP compression
================

Pipeline can also creates a gzipped version of your collected static files,
so that you can avoid compressing them on the fly. ::

  STATICFILES_STORAGE = 'your.app.GZIPCachedStorage'

The storage need to inherit from ``GZIPMixin``: ::

  from django.contrib.staticfiles.storage import CachedStaticFilesStorage

  from pipeline.storage import GZIPMixin


  class GZIPCachedStorage(GZIPMixin, CachedStaticFilesStorage):
      pass


Using with other storages
=========================

You can also use your own custom storage, for example, if you want to use S3 for your assets : ::

  STATICFILES_STORAGE = 'your.app.S3PipelineManifestStorage'

Your storage only needs to inherit from ``PipelineMixin`` and ``ManifestFilesMixin`` or ``CachedFilesMixin``.

In Django 1.7+ you should use `ManifestFilesMixin <https://docs.djangoproject.com/en/1.7/ref/contrib/staticfiles/#manifeststaticfilesstorage>`_
unless you don't have access to the local filesystem in which case you should use ``CachedFilesMixin``. ::

  from django.contrib.staticfiles.storage import CachedFilesMixin, ManifestFilesMixin

  from pipeline.storage import PipelineMixin

  from storages.backends.s3boto import S3BotoStorage

  class S3PipelineManifestStorage(PipelineMixin, ManifestFilesMixin, S3BotoStorage):
      pass

  class S3PipelineCachedStorage(PipelineMixin, CachedFilesMixin, S3BotoStorage):
      pass


Using Pipeline with Bower
=========================

`Bower <http://bower.io/>`_ is a `package manager for the web` that allows
you to easily include frontend components with named versions. Integrating
Bower with Pipeline is straightforward.

Add your Bower directory to your ``STATICFILES_DIRS`` : ::

  STATICFILES_DIRS = (
    os.path.join(os.path.dirname(__file__), '..', 'bower_components'),
  )

Then process the relevant content through Pipeline : ::

  PIPELINE['JAVASCRIPT'] = {
    'components': {
      'source_filenames': (
        'jquery/jquery.js',
        # you can choose to be specific to reduce your payload
        'jquery-ui/ui/*.js',
      ),
      'output_filename': 'js/components.js',
    },
  }

``pipeline.finders.FileSystemFinder`` will help you by excluding much of the
extra content that Bower includes with its components, such as READMEs, tests
and examples, while still including images, fonts, CSS fragments etc.


================================================
FILE: docs/templates.rst
================================================
.. _ref-templates:

====================
Javascript Templates
====================

Pipeline allows you to use javascript templates along with your javascript views.
To use your javascript templates, just add them to your ``JAVASCRIPT`` group ::

  PIPELINE['JAVASCRIPT'] = {
    'application': {
      'source_filenames': (
        'js/application.js',
        'js/templates/**/*.jst',
      ),
      'output_filename': 'js/application.js'
    }
  }

For example, if you have the following template ``js/templates/photo/detail.jst`` ::

  <div class="photo">
   <img src="<%= src %>" />
   <div class="caption">
    <%= caption %>
   </div>
  </div>

It will be available from your javascript code via window.JST ::

  JST.photo_detail({ src:"images/baby-panda.jpg", caption:"A baby panda is born" });


Configuration
-------------

Template function
.................

By default, Pipeline uses a variant of `Micro Templating <http://ejohn.org/blog/javascript-micro-templating/>`_ to compile the templates, but you can choose your preferred JavaScript templating engine by changing ``PIPELINE['TEMPLATE_FUNC']`` ::

  PIPELINE['TEMPLATE_FUNC'] = 'template'

Template namespace
..................

Your templates are made available in a top-level object, by default ``window.JST``,
but you can choose your own via ``PIPELINE['TEMPLATE_NAMESPACE']`` ::

  PIPELINE['TEMPLATE_NAMESPACE'] = 'window.Template'


Template extension
..................

Templates are detected by their extension, by default ``.jst``, but you can use
your own extension via ``PIPELINE['TEMPLATE_EXT']`` ::

  PIPELINE['TEMPLATE_EXT'] = '.mustache'

Template separator
..................

Templates identifier are built using a replacement for directory separator,
by default ``_``, but you specify your own separator via ``PIPELINE['TEMPLATE_SEPARATOR']`` ::

  PIPELINE['TEMPLATE_SEPARATOR'] = '/'


Using it with your favorite template library
--------------------------------------------

Mustache
........

To use it with `Mustache <https://github.com/janl/mustache.js>`_ you will need
some extra javascript ::

  Mustache.template = function(templateString) {
    return function() {
      if (arguments.length < 1) {
        return templateString;
      } else {
        return Mustache.to_html(templateString, arguments[0], arguments[1]);
      }
    };
  };

And use these settings ::

 PIPELINE['TEMPLATE_EXT'] = '.mustache'
 PIPELINE['TEMPLATE_FUNC'] = 'Mustache.template'

Handlebars
..........

To use it with `Handlebars <http://handlebarsjs.com/>`_, use the following settings ::

 PIPELINE['TEMPLATE_EXT'] = '.handlebars'
 PIPELINE['TEMPLATE_FUNC'] = 'Handlebars.compile'
 PIPELINE['TEMPLATE_NAMESPACE'] = 'Handlebars.templates'

Ember.js + Handlebars
.....................

To use it with `Ember.js <http://emberjs.com/>`_, use the following settings ::

 PIPELINE['TEMPLATE_EXT'] = '.handlebars'
 PIPELINE['TEMPLATE_FUNC'] = 'Ember.Handlebars.compile'
 PIPELINE['TEMPLATE_NAMESPACE'] = 'window.Ember.TEMPLATES'
 PIPELINE['TEMPLATE_SEPARATOR'] = '/'

Prototype
.........

To use it with `Prototype <http://www.prototypejs.org/>`_, just setup your
``PIPELINE['TEMPLATE_FUNC']`` ::

  PIPELINE['TEMPLATE_FUNC'] = 'new Template'



================================================
FILE: docs/usage.rst
================================================
.. _ref-usage:

=====
Usage
=====

Describes how to use Pipeline when it is installed and configured.

Templatetags
============

Pipeline includes two template tags: ``stylesheet`` and ``javascript``,
in a template library called ``pipeline``.

They are used to output the ``<link>`` and ``<script>``-tags for the
specified CSS/JavaScript-groups (as specified in the settings).
The first argument must be the name of the CSS/JavaScript group.

When ``settings.DEBUG`` is set to ``True`` the use of these template tags will
result in a separate tag for each resource in a given group (i.e., the
combined, compressed files will not be used), in order to make local debugging
easy. When ``settings.DEBUG`` is set to ``False`` the opposite is true. You can
override the default behavior by setting ``settings.PIPELINE['PIPELINE_ENABLED']``
manually. When set to ``True`` or ``False`` this enables or disables,
respectively, the usage of the combined, compressed file for each resource
group. This can be useful, if you encounter errors in your compressed code that
don't occur in your uncompressed code and you want to debug them locally.

If you need to change the output of the HTML-tags generated from the templatetags,
this can be done by overriding the templates ``pipeline/css.html`` and ``pipeline/js.html``.

Example
-------

If you have specified the CSS-groups “colors” and “stats” and a JavaScript-group
with the name “scripts”, you would use the following code to output them all ::

   {% load pipeline %}
   {% stylesheet 'colors' %}
   {% stylesheet 'stats' %}
   {% javascript 'scripts' %}


Form Media
==========

Django forms and widgets can specify individual CSS or JavaScript files to
include on a page by defining a ``Form.Media`` class with ``css`` and ``js``
attributes.

Pipeline builds upon this by allowing packages to be listed in
``css_packages`` and ``js_packages``. This is equivalent to manually including
these packages in a page's template using the template tags.

To use these, just have your form or widget's ``Media`` class inherit from
``pipeline.forms.PipelineFormMedia`` and define ``css_packages`` and
``js_packages``. You can also continue to reference individual CSS/JavaScript
files using the original ``css``/``js`` attributes, if needed.

Note that unlike the template tags, you cannot customize the HTML for
referencing these files. The ``pipeline/css.html`` and ``pipeline/js.html``
files will not be used. Django takes care of generating the HTML for form and
widget media.


Example
-------

.. code-block:: python

    from django import forms
    from pipeline.forms import PipelineFormMedia


    class MyForm(forms.Form):
        ...

        class Media(PipelineFormMedia):
            css_packages = {
                'all': ('my-styles',)
            }
            js_packages = ('my-scripts',)
            js = ('https://cdn.example.com/some-script.js',)


Collect static
==============

Pipeline integrates with staticfiles, you just need to setup ``STATICFILES_STORAGE`` to ::

    STATICFILES_STORAGE = 'pipeline.storage.PipelineStorage'

Then when you run ``collectstatic`` command, your CSS and your javascripts will be compressed at the same time ::

    $ python manage.py collectstatic

Cache-busting
-------------

Pipeline 1.2+ no longer provides its own cache-busting URL support (using e.g. the ``PIPELINE_VERSIONING`` setting) but uses
Django's built-in staticfiles support for this. To set up cache-busting in conjunction with ``collectstatic`` as above, use ::

    STATICFILES_STORAGE = 'pipeline.storage.PipelineCachedStorage'

This will handle cache-busting just as ``staticfiles``'s built-in ``CachedStaticFilesStorage`` does.

Middleware
==========

To enable HTML compression add ``pipeline.middleware.MinifyHTMLMiddleware``,
to your ``MIDDLEWARE_CLASSES`` settings.

Ensure that it comes after any middleware which modifies your HTML, like ``GZipMiddleware`` ::

   MIDDLEWARE_CLASSES = (
      'django.middleware.gzip.GZipMiddleware',
      'pipeline.middleware.MinifyHTMLMiddleware',
   )


Jinja
=====

Pipeline also includes Jinja2 support and is used almost identically to the Django Template tags implementation.
You just need to pass ``pipeline.jinja2.PipelineExtension`` to your Jinja2 environment::


    {
        'BACKEND': 'django.template.backends.jinja2.Jinja2',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'environment': 'myproject.jinja2.environment',
            'extensions': ['pipeline.jinja2.PipelineExtension']
        }
    }


Templates
---------

Unlike the Django template tag implementation the Jinja2 implementation uses different templates, so if you wish to override them please override pipeline/css.jinja and pipeline/js.jinja.


================================================
FILE: docs/using.rst
================================================
.. _ref-using:

====================
Sites using Pipeline
====================

The following sites are a partial list of people using Pipeline.

Are you using pipeline and not being in this list? Drop us a line. 

20 Minutes
----------

For their internal tools: http://www.20minutes.fr

Borsala
-------

Borsala is the social investment platform. You can follow stock markets that are traded in Turkey: http://borsala.com


Croisé dans le Métro
--------------------

For their main and mobile website:

* http://www.croisedanslemetro.com
* http://m.croisedanslemetro.com

Teachoo
-----------------

Teachoo uses pipeline for compressing all its static files - https://www.teachoo.com

The Molly Project
-----------------

Molly is a framework for the rapid development of information and service
portals targeted at mobile internet devices: https://github.com/mollyproject/mollyproject

It powers the University of Oxford's mobile portal: http://m.ox.ac.uk/

Mozilla
-------

* `mozilla.org <https://mozilla.org>`_ (https://github.com/mozilla/bedrock)
* `Mozilla Developer Network <https://developer.mozilla.org>`_ (https://github.com/mozilla/kuma)

Novapost
--------

For PeopleDoc suite products: http://www.people-doc.com/

Sophicware
----------

Sophicware offers web hosting and DevOps as a service: http://sophicware.com

Ulule
-----

For their main and forum website:

* http://www.ulule.com
* http://vox.ulule.com


================================================
FILE: package.json
================================================
{
  "name": "django-pipeline-tests",
  "private": true,
  "version": "1.0.0",
  "description": "Pipeline is an asset packaging library for Django.",
  "author": "Timothée Peignier <timothee.peignier@tryphon.org>",
  "license": "MIT",
  "readmeFilename": "../README.rst",
  "repository": {
    "type": "git",
    "url": "git://github.com/jazzband/django-pipeline.git"
  },
  "dependencies": {
    "babel-cli": "latest",
    "babel-preset-es2015": "latest",
    "coffeescript": "latest",
    "less": "latest",
    "livescript": "latest",
    "sass": "latest",
    "stylus": "latest",
    "cssmin": "latest",
    "google-closure-compiler": "latest",
    "terser": "latest",
    "uglify-js": "latest",
    "yuglify": "1.0.x",
    "yuicompressor": "latest",
    "typescript": "latest"
  }
}


================================================
FILE: pipeline/__init__.py
================================================
PackageNotFoundError = None
DistributionNotFound = None
try:
    from importlib.metadata import PackageNotFoundError
    from importlib.metadata import version as get_version
except ImportError:
    get_version = None
    PackageNotFoundError = None
if get_version is None:
    try:
        from pkg_resources import DistributionNotFound, get_distribution

        def get_version(x):
            return get_distribution(x).version

    except ImportError:
        get_version = None
        DistributionNotFound = None
        get_distribution = None

__version__ = None
if get_version is not None:
    try:
        __version__ = get_version("django-pipeline")
    except PackageNotFoundError:
        pass
    except DistributionNotFound:
        pass


================================================
FILE: pipeline/collector.py
================================================
import os

import django
from django.contrib.staticfiles import finders
from django.contrib.staticfiles.storage import staticfiles_storage

from pipeline.finders import PipelineFinder


class Collector:
    request = None

    def __init__(self, storage=None):
        if storage is None:
            storage = staticfiles_storage
        self.storage = storage

    def _get_modified_time(self, storage, prefixed_path):
        if django.VERSION[:2] >= (1, 10):
            return storage.get_modified_time(prefixed_path)
        return storage.modified_time(prefixed_path)

    def clear(self, path=""):
        dirs, files = self.storage.listdir(path)
        for f in files:
            fpath = os.path.join(path, f)
            self.storage.delete(fpath)
        for d in dirs:
            self.clear(os.path.join(path, d))

    def collect(self, request=None, files=[]):
        if self.request and self.request is request:
            return
        self.request = request
        found_files = {}
        for finder in finders.get_finders():
            # Ignore our finder to avoid looping
            if isinstance(finder, PipelineFinder):
                continue
            for path, storage in finder.list(["CVS", ".*", "*~"]):
                # Prefix the relative path if the source storage contains it
                if getattr(storage, "prefix", None):
                    prefixed_path = os.path.join(storage.prefix, path)
                else:
                    prefixed_path = path

                if prefixed_path not in found_files and (
                    not files or prefixed_path in files
                ):
                    found_files[prefixed_path] = (storage, path)
                    self.copy_file(path, prefixed_path, storage)

                if files and len(files) == len(found_files):
                    break

        return found_files.keys()

    def copy_file(self, path, prefixed_path, source_storage):
        # Delete the target file if needed or break
        if not self.delete_file(path, prefixed_path, source_storage):
            return
        # Finally start copying
        with source_storage.open(path) as source_file:
            self.storage.save(prefixed_path, source_file)

    def delete_file(self, path, prefixed_path, source_storage):
        if self.storage.exists(prefixed_path):
            try:
                # When was the target file modified last time?
                target_last_modified = self._get_modified_time(
                    self.storage,
                    prefixed_path,
                )
            except (OSError, NotImplementedError, AttributeError):
                # The storage doesn't support ``modified_time`` or failed
                pass
            else:
                try:
                    # When was the source file modified last time?
                    source_last_modified = self._get_modified_time(source_storage, path)
                except (OSError, NotImplementedError, AttributeError):
                    pass
                else:
                    # Skip the file if the source file is younger
                    # Avoid sub-second precision
                    if target_last_modified.replace(
                        microsecond=0
                    ) >= source_last_modified.replace(microsecond=0):
                        return False
            # Then delete the existing file if really needed
            self.storage.delete(prefixed_path)
        return True


default_collector = Collector()


================================================
FILE: pipeline/compilers/__init__.py
================================================
import os
import shutil
import subprocess
from tempfile import NamedTemporaryFile

from django.contrib.staticfiles import finders
from django.contrib.staticfiles.storage import staticfiles_storage
from django.core.files.base import ContentFile
from django.utils.encoding import force_str

from pipeline.conf import settings
from pipeline.exceptions import CompilerError
from pipeline.utils import set_std_streams_blocking, to_class


class Compiler:
    def __init__(self, storage=None, verbose=False):
        if storage is None:
            storage = staticfiles_storage
        self.storage = storage
        self.verbose = verbose

    @property
    def compilers(self):
        return [to_class(compiler) for compiler in settings.COMPILERS]

    def compile(self, paths, compiler_options={}, force=False):
        def _compile(input_path):
            for compiler in self.compilers:
                compiler = compiler(verbose=self.verbose, storage=self.storage)
                if compiler.match_file(input_path):
                    try:
                        infile = self.storage.path(input_path)
                    except NotImplementedError:
                        infile = finders.find(input_path)
                    project_infile = finders.find(input_path)
                    outfile = compiler.output_path(infile, compiler.output_extension)
                    outdated = compiler.is_outdated(project_infile, outfile)
                    compiler.compile_file(
                        project_infile,
                        outfile,
                        outdated=outdated,
                        force=force,
                        **compiler_options,
                    )

                    return compiler.output_path(input_path, compiler.output_extension)
            else:
                return input_path

        try:
            import multiprocessing  # noqa: PLC0415
            from concurrent import futures  # noqa: PLC0415
        except ImportError:
            return list(map(_compile, paths))
        else:
            with futures.ThreadPoolExecutor(
                max_workers=multiprocessing.cpu_count()
            ) as executor:
                return list(executor.map(_compile, paths))


class CompilerBase:
    def __init__(self, verbose, storage):
        self.verbose = verbose
        self.storage = storage

    def match_file(self, filename):
        raise NotImplementedError

    def compile_file(self, infile, outfile, outdated=False, force=False):
        raise NotImplementedError

    def save_file(self, path, content):
        return self.storage.save(path, ContentFile(content))

    def read_file(self, path):
        file = self.storage.open(path, "rb")
        content = file.read()
        file.close()
        return content

    def output_path(self, path, extension):
        path = os.path.splitext(path)
        return ".".join((path[0], extension))

    def is_outdated(self, infile, outfile):
        if not os.path.exists(outfile):
            return True

        try:
            return os.path.getmtime(infile) > os.path.getmtime(outfile)
        except OSError:
            return True


class SubProcessCompiler(CompilerBase):
    def execute_command(self, command, cwd=None, stdout_captured=None):
        """Execute a command at cwd, saving its normal output at
        stdout_captured. Errors, defined as nonzero return code or a failure
        to start execution, will raise a CompilerError exception with a
        description of the cause. They do not write output.

        This is file-system safe (any valid file names are allowed, even with
        spaces or crazy characters) and OS agnostic (existing and future OSes
        that Python supports should already work).

        The only thing weird here is that any incoming command arg item may
        itself be a tuple. This allows compiler implementations to look clean
        while supporting historical string config settings and maintaining
        backwards compatibility. Thus, we flatten one layer deep.
         ((env, foocomp), infile, (-arg,)) -> (env, foocomp, infile, -arg)
        """
        argument_list = []
        for flattening_arg in command:
            if isinstance(flattening_arg, (str,)):
                argument_list.append(flattening_arg)
            else:
                argument_list.extend(flattening_arg)

        # The first element in argument_list is the program that will be
        # executed; if it is '', then a PermissionError will be raised.
        # Thus empty arguments are filtered out from argument_list
        argument_list = list(filter(None, argument_list))
        stdout = None
        try:
            # We always catch stdout in a file, but we may not have a use for it.
            temp_file_container = (
                cwd or os.path.dirname(stdout_captured or "") or os.getcwd()
            )
            with NamedTemporaryFile(
                "wb", delete=False, dir=temp_file_container
            ) as stdout:
                compiling = subprocess.Popen(
                    argument_list, cwd=cwd, stdout=stdout, stderr=subprocess.PIPE
                )
                _, stderr = compiling.communicate()
                set_std_streams_blocking()

            if compiling.returncode != 0:
                stdout_captured = None  # Don't save erroneous result.
                raise CompilerError(
                    f"{argument_list!r} exit code {compiling.returncode}\n{stderr}",
                    command=argument_list,
                    error_output=force_str(stderr),
                )

            # User wants to see everything that happened.
            if self.verbose:
                with open(stdout.name, "rb") as out:
                    print(out.read())
                print(stderr)
        except OSError as e:
            stdout_captured = None  # Don't save erroneous result.
            raise CompilerError(e, command=argument_list, error_output=str(e))
        finally:
            # Decide what to do with captured stdout.
            if stdout:
                if stdout_captured:
                    shutil.move(
                        stdout.name, os.path.join(cwd or os.curdir, stdout_captured)
                    )
                else:
                    os.remove(stdout.name)


================================================
FILE: pipeline/compilers/coffee.py
================================================
from pipeline.compilers import SubProcessCompiler
from pipeline.conf import settings


class CoffeeScriptCompiler(SubProcessCompiler):
    output_extension = "js"

    def match_file(self, path):
        return path.endswith(".coffee") or path.endswith(".litcoffee")

    def compile_file(self, infile, outfile, outdated=False, force=False):
        if not outdated and not force:
            return  # File doesn't need to be recompiled
        command = (
            settings.COFFEE_SCRIPT_BINARY,
            "-cp",
            settings.COFFEE_SCRIPT_ARGUMENTS,
            infile,
        )
        return self.execute_command(command, stdout_captured=outfile)


================================================
FILE: pipeline/compilers/es6.py
================================================
from pipeline.compilers import SubProcessCompiler
from pipeline.conf import settings


class ES6Compiler(SubProcessCompiler):
    output_extension = "js"

    def match_file(self, path):
        return path.endswith(".es6")

    def compile_file(self, infile, outfile, outdated=False, force=False):
        if not outdated and not force:
            return  # File doesn't need to be recompiled
        command = (
            settings.BABEL_BINARY,
            settings.BABEL_ARGUMENTS,
            infile,
            "-o",
            outfile,
        )
        return self.execute_command(command)


================================================
FILE: pipeline/compilers/less.py
================================================
from os.path import dirname

from pipeline.compilers import SubProcessCompiler
from pipeline.conf import settings


class LessCompiler(SubProcessCompiler):
    output_extension = "css"

    def match_file(self, filename):
        return filename.endswith(".less")

    def compile_file(self, infile, outfile, outdated=False, force=False):
        # Pipe to file rather than provide outfile arg due to a bug in lessc
        command = (
            settings.LESS_BINARY,
            settings.LESS_ARGUMENTS,
            infile,
        )
        return self.execute_command(
            command, cwd=dirname(infile), stdout_captured=outfile
        )


================================================
FILE: pipeline/compilers/livescript.py
================================================
from pipeline.compilers import SubProcessCompiler
from pipeline.conf import settings


class LiveScriptCompiler(SubProcessCompiler):
    output_extension = "js"

    def match_file(self, path):
        return path.endswith(".ls")

    def compile_file(self, infile, outfile, outdated=False, force=False):
        if not outdated and not force:
            return  # File doesn't need to be recompiled
        command = (
            settings.LIVE_SCRIPT_BINARY,
            "-cp",
            settings.LIVE_SCRIPT_ARGUMENTS,
            infile,
        )
        return self.execute_command(command, stdout_captured=outfile)


================================================
FILE: pipeline/compilers/sass.py
================================================
from os.path import dirname

from pipeline.compilers import SubProcessCompiler
from pipeline.conf import settings


class SASSCompiler(SubProcessCompiler):
    output_extension = "css"

    def match_file(self, filename):
        return filename.endswith((".scss", ".sass"))

    def compile_file(self, infile, outfile, outdated=False, force=False):
        command = (settings.SASS_BINARY, settings.SASS_ARGUMENTS, infile, outfile)
        return self.execute_command(command, cwd=dirname(infile))


================================================
FILE: pipeline/compilers/stylus.py
================================================
from os.path import dirname

from pipeline.compilers import SubProcessCompiler
from pipeline.conf import settings


class StylusCompiler(SubProcessCompiler):
    output_extension = "css"

    def match_file(self, filename):
        return filename.endswith(".styl")

    def compile_file(self, infile, outfile, outdated=False, force=False):
        command = (settings.STYLUS_BINARY, settings.STYLUS_ARGUMENTS, infile)
        return self.execute_command(command, cwd=dirname(infile))


================================================
FILE: pipeline/compilers/typescript.py
================================================
from pipeline.compilers import SubProcessCompiler
from pipeline.conf import settings


class TypeScriptCompiler(SubProcessCompiler):
    output_extension = "js"

    def match_file(self, path):
        return path.endswith(".ts")

    def compile_file(self, infile, outfile, outdated=False, force=False):
        if not outdated and not force:
            return
        command = (
            settings.TYPE_SCRIPT_BINARY,
            settings.TYPE_SCRIPT_ARGUMENTS,
            infile,
            "--outFile",
            outfile,
        )
        return self.execute_command(command)


================================================
FILE: pipeline/compressors/__init__.py
================================================
from __future__ import annotations

import base64
import os
import posixpath
import re
import subprocess
import warnings
from collections.abc import Iterator, Sequence
from itertools import takewhile

from django.contrib.staticfiles.storage import staticfiles_storage
from django.utils.encoding import force_str, smart_bytes

from pipeline.conf import settings
from pipeline.exceptions import CompressorError
from pipeline.utils import relpath, set_std_streams_blocking, to_class

# Regex matching url(...), url('...'), and url("...") patterns.
#
# Replacements will preserve the quotes and any whitespace contained within
# the pattern, transforming only the filename.
#
# Verbose and documented, to ease future maintenance.
_CSS_URL_REWRITE_PATH_RE_STR = r"""
    (?P<url_prefix>
      url\(                 # The opening `url(`.
      (?P<url_quote>['"]?)  # Optional quote (' or ").
      \s*
    )
    (?P<url_path>.*?)       # The path to capture.
    (?P<url_suffix>
      (?P=url_quote)        # The quote found earlier, if any.
      \s*
      \)                    # The end `)`, completing `url(...)`.
    )
"""


# Regex matching `//@ sourceMappingURL=...` and variants.
#
# This will capture sourceMappingURL and sourceURL keywords, both
# `//@` and `//#` variants, and both `//` and `/* ... */` comment types.
#
# Verbose and documented, to ease future maintenance.
_SOURCEMAP_REWRITE_PATH_RE_STR = r"""
    (?P<sourcemap_prefix>
      /(?:/|(?P<sourcemap_mlcomment>\*))  # Opening comment (`//#`, `//@`,
      [#@]\s+                             # `/*@`, `/*#`).
      source(?:Mapping)?URL=              # The sourcemap indicator.
      \s*
    )
    (?P<sourcemap_path>.*?)               # The path to capture.
    (?P<sourcemap_suffix>
      \s*
      (?(sourcemap_mlcomment)\*/\s*)      # End comment (`*/`)
    )
    $                                     # The line should now end.
"""


# Implementation of the above regexes, for CSS and JavaScript.
CSS_REWRITE_PATH_RE = re.compile(
    f"{_CSS_URL_REWRITE_PATH_RE_STR}|{_SOURCEMAP_REWRITE_PATH_RE_STR}", re.X | re.M
)
JS_REWRITE_PATH_RE = re.compile(_SOURCEMAP_REWRITE_PATH_RE_STR, re.X | re.M)


URL_REPLACER = re.compile(r"""url\(__EMBED__(.+?)(\?\d+)?\)""")
NON_REWRITABLE_URL = re.compile(r"^(#|http:|https:|data:|//)")

DEFAULT_TEMPLATE_FUNC = "template"
TEMPLATE_FUNC = r"""var template = function(str){var fn = new Function('obj', 'var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push(\''+str.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/<%=([\s\S]+?)%>/g,function(match,code){return "',"+code.replace(/\\'/g, "'")+",'";}).replace(/<%([\s\S]+?)%>/g,function(match,code){return "');"+code.replace(/\\'/g, "'").replace(/[\r\n\t]/g,' ')+"__p.push('";}).replace(/\r/g,'\\r').replace(/\n/g,'\\n').replace(/\t/g,'\\t')+"');}return __p.join('');");return fn;};"""  # noqa

MIME_TYPES = {
    ".png": "image/png",
    ".jpg": "image/jpeg",
    ".jpeg": "image/jpeg",
    ".gif": "image/gif",
    ".tif": "image/tiff",
    ".tiff": "image/tiff",
    ".ttf": "font/truetype",
    ".otf": "font/opentype",
    ".woff": "font/woff",
}
EMBED_EXTS = MIME_TYPES.keys()
FONT_EXTS = [".ttf", ".otf", ".woff"]


class Compressor:
    asset_contents = {}

    def __init__(self, storage=None, verbose=False):
        if storage is None:
            storage = staticfiles_storage
        self.storage = storage
        self.verbose = verbose

    @property
    def js_compressor(self):
        return to_class(settings.JS_COMPRESSOR)

    @property
    def css_compressor(self):
        return to_class(settings.CSS_COMPRESSOR)

    def compress_js(
        self,
        paths: Sequence[str],
        templates: Sequence[str] | None = None,
        *,
        output_filename: str | None = None,
        **kwargs,
    ) -> str:
        """Concatenate and compress JS files"""
        # Note how a semicolon is added between the two files to make sure that
        # their behavior is not changed. '(expression1)\n(expression2)' calls
        # `expression1` with `expression2` as an argument! Superfluous
        # semicolons are valid in JavaScript and will be removed by the
        # minifier.
        js = self.concatenate(
            paths,
            file_sep=";",
            output_filename=output_filename,
            rewrite_path_re=JS_REWRITE_PATH_RE,
        )

        if templates:
            js = js + self.compile_templates(templates)

        if not settings.DISABLE_WRAPPER:
            js = settings.JS_WRAPPER % js

        compressor = self.js_compressor
        if compressor:
            js = getattr(compressor(verbose=self.verbose), "compress_js")(js)

        return js

    def compress_css(self, paths, output_filename, variant=None, **kwargs):
        """Concatenate and compress CSS files"""
        css = self.concatenate(
            paths,
            file_sep="",
            rewrite_path_re=CSS_REWRITE_PATH_RE,
            output_filename=output_filename,
            variant=variant,
        )
        compressor = self.css_compressor
        if compressor:
            css = getattr(compressor(verbose=self.verbose), "compress_css")(css)
        if not variant:
            return css
        elif variant == "datauri":
            return self.with_data_uri(css)
        else:
            raise CompressorError(f'"{variant}" is not a valid variant')

    def compile_templates(self, paths):
        compiled = []
        if not paths:
            return ""
        namespace = settings.TEMPLATE_NAMESPACE
        base_path = self.base_path(paths)
        for path in paths:
            contents = self.read_text(path)
            contents = re.sub("\r?\n", "\\\\n", contents)
            contents = re.sub("'", "\\'", contents)
            name = self.template_name(path, base_path)
            compiled.append(
                "{}['{}'] = {}('{}');\n".format(
                    namespace, name, settings.TEMPLATE_FUNC, contents
                )
            )
        if settings.TEMPLATE_FUNC == DEFAULT_TEMPLATE_FUNC:
            compiler = TEMPLATE_FUNC
        else:
            compiler = ""
        return "\n".join(
            [
                "{namespace} = {namespace} || {{}};".format(namespace=namespace),
                compiler,
                "".join(compiled),
            ]
        )

    def base_path(self, paths):
        def names_equal(name):
            return all(n == name[0] for n in name[1:])

        directory_levels = zip(*[p.split(os.sep) for p in paths])
        return os.sep.join(x[0] for x in takewhile(names_equal, directory_levels))

    def template_name(self, path, base):
        """Find out the name of a JS template"""
        if not base:
            path = os.path.basename(path)
        if path == base:
            base = os.path.dirname(path)
        name = re.sub(
            r"^{}[\/\\]?(.*){}$".format(
                re.escape(base), re.escape(settings.TEMPLATE_EXT)
            ),
            r"\1",
            path,
        )
        return re.sub(r"[\/\\]", settings.TEMPLATE_SEPARATOR, name)

    def concatenate_and_rewrite(self, paths, output_filename, variant=None):
        """Concatenate together files and rewrite urls"""
        warnings.warn(
            "Compressor.concatenate_and_rewrite() is deprecated. Please "
            "call concatenate() instead.",
            DeprecationWarning,
            stacklevel=2,
        )

        return self.concatenate(
            paths=paths,
            file_sep="",
            rewrite_path_re=CSS_REWRITE_PATH_RE,
            output_filename=output_filename,
            variant=variant,
        )

    def concatenate(
        self,
        paths: Sequence[str],
        *,
        file_sep: str | None = None,
        output_filename: str | None = None,
        rewrite_path_re: re.Pattern | None = None,
        variant: str | None = None,
    ) -> str:
        """Concatenate together a list of files.

        The caller can specify a delimiter between files and any regexes
        used to normalize relative paths. Path normalization is important for
        ensuring that local resources or sourcemaps can be updated in time
        for Django's static media post-processing phase.
        """

        def _reconstruct(
            m: re.Match,
            source_path: str,
        ) -> str:
            groups = m.groupdict()
            asset_path: str | None = None
            prefix = ""
            suffix = ""

            for prefix in ("sourcemap", "url"):
                asset_path = groups.get(f"{prefix}_path")

                if asset_path is not None:
                    asset_path = asset_path.strip()
                    prefix, suffix = m.group(f"{prefix}_prefix", f"{prefix}_suffix")
                    break

            if asset_path is None:
                # This is empty. Return the whole match as-is.
                return m.group()

            if asset_path and not NON_REWRITABLE_URL.match(asset_path):
                asset_path = self.construct_asset_path(
                    asset_path=asset_path,
                    source_path=source_path,
                    output_filename=output_filename,
                    variant=variant,
                )

            return f"{prefix}{asset_path}{suffix}"

        def _iter_files() -> Iterator[str]:
            if not output_filename or not rewrite_path_re:
                # This is legacy call, which does not support sourcemap-aware
                # asset rewriting. Pipeline itself won't invoke this outside
                # of tests, but it maybe important for third-parties who
                # are specializing these classes.
                warnings.warn(
                    "Compressor.concatenate() was called without passing "
                    "rewrite_path_re_= or output_filename=. If you are "
                    "specializing Compressor, please update your call "
                    "to remain compatible with future changes.",
                    DeprecationWarning,
                    stacklevel=3,
                )

                return (self.read_text(path) for path in paths)

            # Now that we can attempt the modern support for concatenating
            # files, handling rewriting of relative assets in the process.
            return (
                rewrite_path_re.sub(
                    lambda m: _reconstruct(m, path), self.read_text(path)
                )
                for path in paths
            )

        if file_sep is None:
            warnings.warn(
                "Compressor.concatenate() was called without passing "
                "file_sep=. If you are specializing Compressor, please "
                "update your call to remain compatible with future changes. "
                "Defaulting to JavaScript behavior for "
                "backwards-compatibility.",
                DeprecationWarning,
                stacklevel=2,
            )
            file_sep = ";"

        return f"\n{file_sep}".join(_iter_files())

    def construct_asset_path(
        self, asset_path, source_path, output_filename, variant=None
    ):
        """Return a rewritten asset URL for a stylesheet or JavaScript file."""
        public_path = self.absolute_path(
            asset_path,
            os.path.dirname(source_path).replace("\\", "/"),
        )
        if self.embeddable(public_path, variant):
            return f"__EMBED__{public_path}"
        if not posixpath.isabs(asset_path):
            asset_path = self.relative_path(public_path, output_filename)
        return asset_path

    def embeddable(self, path, variant):
        """Is the asset embeddable ?"""
        name, ext = os.path.splitext(path)
        font = ext in FONT_EXTS
        if not variant:
            return False
        if not (
            re.search(settings.EMBED_PATH, path.replace("\\", "/"))
            and self.storage.exists(path)
        ):
            return False
        if ext not in EMBED_EXTS:
            return False
        if not (
            font or len(self.encoded_content(path)) < settings.EMBED_MAX_IMAGE_SIZE
        ):
            return False
        return True

    def with_data_uri(self, css):
        def datauri(match):
            path = match.group(1)
            mime_type = self.mime_type(path)
            data = self.encoded_content(path)
            return f'url("data:{mime_type};charset=utf-8;base64,{data}")'

        return URL_REPLACER.sub(datauri, css)

    def encoded_content(self, path):
        """Return the base64 encoded contents"""
        if path in self.__class__.asset_contents:
            return self.__class__.asset_contents[path]
        data = self.read_bytes(path)
        self.__class__.asset_contents[path] = force_str(base64.b64encode(data))
        return self.__class__.asset_contents[path]

    def mime_type(self, path):
        """Get mime-type from filename"""
        name, ext = os.path.splitext(path)
        return MIME_TYPES[ext]

    def absolute_path(self, path, start):
        """
        Return the absolute public path for an asset,
        given the path of the stylesheet that contains it.
        """
        if posixpath.isabs(path):
            path = posixpath.join(staticfiles_storage.location, path)
        else:
            path = posixpath.join(start, path)
        return posixpath.normpath(path)

    def relative_path(self, absolute_path, output_filename):
        """Rewrite paths relative to the output stylesheet path"""
        absolute_path = posixpath.join(settings.PIPELINE_ROOT, absolute_path)
        output_path = posixpath.join(
            settings.PIPELINE_ROOT, posixpath.dirname(output_filename)
        )
        return relpath(absolute_path, output_path)

    def read_bytes(self, path):
        """Read file content in binary mode"""
        file = staticfiles_storage.open(path)
        content = file.read()
        file.close()
        return content

    def read_text(self, path):
        content = self.read_bytes(path)
        return force_str(content)


class CompressorBase:
    def __init__(self, verbose):
        self.verbose = verbose

    def filter_css(self, css):
        raise NotImplementedError

    def filter_js(self, js):
        raise NotImplementedError


class SubProcessCompressor(CompressorBase):
    def execute_command(self, command, content):
        argument_list = []
        for flattening_arg in command:
            if isinstance(flattening_arg, (str,)):
                argument_list.append(flattening_arg)
            else:
                argument_list.extend(flattening_arg)

        pipe = subprocess.Popen(
            argument_list,
            stdout=subprocess.PIPE,
            stdin=subprocess.PIPE,
            stderr=subprocess.PIPE,
        )
        if content:
            content = smart_bytes(content)
        stdout, stderr = pipe.communicate(content)
        set_std_streams_blocking()
        if stderr.strip() and pipe.returncode != 0:
            raise CompressorError(force_str(stderr))
        elif self.verbose:
            print(force_str(stderr))
        return force_str(stdout)


class NoopCompressor(CompressorBase):
    def compress_js(self, js):
        return js

    def compress_css(self, css):
        return css


================================================
FILE: pipeline/compressors/closure.py
================================================
from pipeline.compressors import SubProcessCompressor
from pipeline.conf import settings


class ClosureCompressor(SubProcessCompressor):
    def compress_js(self, js):
        command = (settings.CLOSURE_BINARY, settings.CLOSURE_ARGUMENTS)
        return self.execute_command(command, js)


================================================
FILE: pipeline/compressors/csshtmljsminify.py
================================================
from pipeline.compressors import CompressorBase


class CssHtmlJsMinifyCompressor(CompressorBase):
    """
    CSS, HTML and JS compressor based on the Python library css-html-js-minify
    (https://pypi.org/project/css-html-js-minify/).
    """

    def compress_css(self, css):
        from css_html_js_minify import css_minify  # noqa: PLC0415

        return css_minify(css)

    def compress_js(self, js):
        from css_html_js_minify import js_minify  # noqa: PLC0415

        return js_minify(js)


================================================
FILE: pipeline/compressors/cssmin.py
================================================
from pipeline.compressors import SubProcessCompressor
from pipeline.conf import settings


class CSSMinCompressor(SubProcessCompressor):
    def compress_css(self, css):
        command = (settings.CSSMIN_BINARY, settings.CSSMIN_ARGUMENTS)
        return self.execute_command(command, css)


================================================
FILE: pipeline/compressors/csstidy.py
================================================
from django.core.files import temp as tempfile

from pipeline.compressors import SubProcessCompressor
from pipeline.conf import settings


class CSSTidyCompressor(SubProcessCompressor):
    def compress_css(self, css):
        output_file = tempfile.NamedTemporaryFile(suffix=".pipeline")

        command = (
            settings.CSSTIDY_BINARY,
            "-",
            settings.CSSTIDY_ARGUMENTS,
            output_file.name,
        )
        self.execute_command(command, css)

        filtered_css = output_file.read()
        output_file.close()
        return filtered_css


================================================
FILE: pipeline/compressors/jsmin.py
================================================
from pipeline.compressors import CompressorBase


class JSMinCompressor(CompressorBase):
    """
    JS compressor based on the Python library jsmin
    (http://pypi.python.org/pypi/jsmin/).
    """

    def compress_js(self, js):
        from jsmin import jsmin  # noqa: PLC0415

        return jsmin(js)


================================================
FILE: pipeline/compressors/terser.py
================================================
from pipeline.compressors import SubProcessCompressor
from pipeline.conf import settings


class TerserCompressor(SubProcessCompressor):
    def compress_js(self, js):
        command = (settings.TERSER_BINARY, settings.TERSER_ARGUMENTS)
        if self.verbose:
            command += " --verbose"
        return self.execute_command(command, js)


================================================
FILE: pipeline/compressors/uglifyjs.py
================================================
from pipeline.compressors import SubProcessCompressor
from pipeline.conf import settings


class UglifyJSCompressor(SubProcessCompressor):
    def compress_js(self, js):
        command = (settings.UGLIFYJS_BINARY, settings.UGLIFYJS_ARGUMENTS)
        if self.verbose:
            command += " --verbose"
        return self.execute_command(command, js)


================================================
FILE: pipeline/compressors/yuglify.py
================================================
from pipeline.compressors import SubProcessCompressor
from pipeline.conf import settings


class YuglifyCompressor(SubProcessCompressor):
    def compress_common(self, content, compress_type, arguments):
        command = (settings.YUGLIFY_BINARY, f"--type={compress_type}", arguments)
        return self.execute_command(command, content)

    def compress_js(self, js):
        return self.compress_common(js, "js", settings.YUGLIFY_JS_ARGUMENTS)

    def compress_css(self, css):
        return self.compress_common(css, "css", settings.YUGLIFY_CSS_ARGUMENTS)


================================================
FILE: pipeline/compressors/yui.py
================================================
from pipeline.compressors import SubProcessCompressor
from pipeline.conf import settings


class YUICompressor(SubProcessCompressor):
    def compress_common(self, content, compress_type, arguments):
        command = (settings.YUI_BINARY, f"--type={compress_type}", arguments)
        return self.execute_command(command, content)

    def compress_js(self, js):
        return self.compress_common(js, "js", settings.YUI_JS_ARGUMENTS)

    def compress_css(self, css):
        return self.compress_common(css, "css", settings.YUI_CSS_ARGUMENTS)


================================================
FILE: pipeline/conf.py
================================================
import os
import shlex
from collections.abc import MutableMapping

from django.conf import settings as _settings
from django.core.signals import setting_changed
from django.dispatch import receiver

DEFAULTS = {
    "PIPELINE_ENABLED": not _settings.DEBUG,
    "PIPELINE_COLLECTOR_ENABLED": True,
    "PIPELINE_ROOT": _settings.STATIC_ROOT,
    "PIPELINE_URL": _settings.STATIC_URL,
    "SHOW_ERRORS_INLINE": _settings.DEBUG,
    "CSS_COMPRESSOR": "pipeline.compressors.yuglify.YuglifyCompressor",
    "JS_COMPRESSOR": "pipeline.compressors.yuglify.YuglifyCompressor",
    "COMPILERS": [],
    "STYLESHEETS": {},
    "JAVASCRIPT": {},
    "TEMPLATE_NAMESPACE": "window.JST",
    "TEMPLATE_EXT": ".jst",
    "TEMPLATE_FUNC": "template",
    "TEMPLATE_SEPARATOR": "_",
    "DISABLE_WRAPPER": False,
    "JS_WRAPPER": "(function() {\n%s\n}).call(this);",
    "CSSTIDY_BINARY": "/usr/bin/env csstidy",
    "CSSTIDY_ARGUMENTS": "--template=highest",
    "YUGLIFY_BINARY": "/usr/bin/env yuglify",
    "YUGLIFY_CSS_ARGUMENTS": "--terminal",
    "YUGLIFY_JS_ARGUMENTS": "--terminal",
    "YUI_BINARY": "/usr/bin/env yuicompressor",
    "YUI_CSS_ARGUMENTS": "",
    "YUI_JS_ARGUMENTS": "",
    "CLOSURE_BINARY": "/usr/bin/env closure",
    "CLOSURE_ARGUMENTS": "",
    "UGLIFYJS_BINARY": "/usr/bin/env uglifyjs",
    "UGLIFYJS_ARGUMENTS": "",
    "TERSER_BINARY": "/usr/bin/env terser",
    "TERSER_ARGUMENTS": "--compress",
    "CSSMIN_BINARY": "/usr/bin/env cssmin",
    "CSSMIN_ARGUMENTS": "",
    "COFFEE_SCRIPT_BINARY": "/usr/bin/env coffee",
    "COFFEE_SCRIPT_ARGUMENTS": "",
    "BABEL_BINARY": "/usr/bin/env babel",
    "BABEL_ARGUMENTS": "",
    "LIVE_SCRIPT_BINARY": "/usr/bin/env lsc",
    "LIVE_SCRIPT_ARGUMENTS": "",
    "TYPE_SCRIPT_BINARY": "/usr/bin/env tsc",
    "TYPE_SCRIPT_ARGUMENTS": "",
    "SASS_BINARY": "/usr/bin/env sass",
    "SASS_ARGUMENTS": "",
    "STYLUS_BINARY": "/usr/bin/env stylus",
    "STYLUS_ARGUMENTS": "",
    "LESS_BINARY": "/usr/bin/env lessc",
    "LESS_ARGUMENTS": "",
    "MIMETYPES": (
        (("text/coffeescript"), (".coffee")),
        (("text/less"), (".less")),
        (("text/javascript"), (".js")),
        (("text/typescript"), (".ts")),
        (("text/x-sass"), (".sass")),
        (("text/x-scss"), (".scss")),
    ),
    "EMBED_MAX_IMAGE_SIZE": 32700,
    "EMBED_PATH": r"[/]?embed/",
}


class PipelineSettings(MutableMapping):
    """
    Container object for pipeline settings
    """

    def __init__(self, wrapped_settings):
        self.settings = DEFAULTS.copy()
        self.settings.update(wrapped_settings)

    def __getitem__(self, key):
        value = self.settings[key]
        if key.endswith(("_BINARY", "_ARGUMENTS")):
            if isinstance(value, (str,)):
                return tuple(shlex.split(value, posix=(os.name == "posix")))
            return tuple(value)
        return value

    def __setitem__(self, key, value):
        self.settings[key] = value

    def __delitem__(self, key):
        del self.store[key]

    def __iter__(self):
        return iter(self.settings)

    def __len__(self):
        return len(self.settings)

    def __getattr__(self, name):
        return self.__getitem__(name)


settings = PipelineSettings(_settings.PIPELINE)


@receiver(setting_changed)
def reload_settings(**kwargs):
    if kwargs["setting"] == "PIPELINE":
        settings.update(kwargs["value"])


================================================
FILE: pipeline/exceptions.py
================================================
class PipelineException(Exception):
    pass


class PackageNotFound(PipelineException):
    pass


class CompilerError(PipelineException):
    def __init__(self, msg, command=None, error_output=None):
        super().__init__(msg)

        self.command = command
        self.error_output = error_output.strip()


class CompressorError(PipelineException):
    pass


================================================
FILE: pipeline/finders.py
================================================
from itertools import chain
from os.path import normpath

from django.contrib.staticfiles.finders import (
    AppDirectoriesFinder as DjangoAppDirectoriesFinder,
)
from django.contrib.staticfiles.finders import BaseFinder, BaseStorageFinder
from django.contrib.staticfiles.finders import (
    FileSystemFinder as DjangoFileSystemFinder,
)
from django.contrib.staticfiles.finders import find
from django.contrib.staticfiles.storage import staticfiles_storage
from django.utils._os import safe_join

from pipeline.conf import settings


class PipelineFinder(BaseStorageFinder):
    storage = staticfiles_storage

    def find(self, path, **kwargs):
        if not settings.PIPELINE_ENABLED:
            return super().find(path, **kwargs)
        else:
            return []

    def list(self, ignore_patterns):
        return []


class ManifestFinder(BaseFinder):
    def find(self, path, **kwargs):
        """
        Looks for files in PIPELINE.STYLESHEETS and PIPELINE.JAVASCRIPT
        """
        matches = []
        for elem in chain(settings.STYLESHEETS.values(), settings.JAVASCRIPT.values()):
            if normpath(elem["output_filename"]) == normpath(path):
                match = safe_join(settings.PIPELINE_ROOT, path)
                if not kwargs.get("find_all", kwargs.get("all", False)):
                    return match
                matches.append(match)
        return matches

    def list(self, *args):
        return []


class CachedFileFinder(BaseFinder):
    def find(self, path, **kwargs):
        """
        Work out the uncached name of the file and look that up instead
        """
        try:
            start, _, extn = path.rsplit(".", 2)
        except ValueError:
            return []
        path = ".".join((start, extn))
        return find(path, **kwargs) or []

    def list(self, *args):
        return []


class PatternFilterMixin:
    ignore_patterns = []

    def get_ignored_patterns(self):
        return list(set(self.ignore_patterns))

    def list(self, ignore_patterns):
        if ignore_patterns:
            ignore_patterns = ignore_patterns + self.get_ignored_patterns()
        return super().list(ignore_patterns)


class AppDirectoriesFinder(PatternFilterMixin, DjangoAppDirectoriesFinder):
    """
    Like AppDirectoriesFinder, but doesn't return any additional ignored
    patterns.

    This allows us to concentrate/compress our components without dragging
    the raw versions in via collectstatic.
    """

    ignore_patterns = [
        "*.js",
        "*.css",
        "*.less",
        "*.scss",
        "*.styl",
    ]


class FileSystemFinder(PatternFilterMixin, DjangoFileSystemFinder):
    """
    Like FileSystemFinder, but doesn't return any additional ignored patterns

    This allows us to concentrate/compress our components without dragging
    the raw versions in too.
    """

    ignore_patterns = [
        "*.js",
        "*.css",
        "*.less",
        "*.scss",
        "*.styl",
        "*.sh",
        "*.html",
        "*.md",
        "*.markdown",
        "*.php",
        "*.txt",
        "README*",
        "LICENSE*",
        "*examples*",
        "*test*",
        "*bin*",
        "*samples*",
        "*docs*",
        "*build*",
        "*demo*",
        "Makefile*",
        "Gemfile*",
        "node_modules",
    ]


================================================
FILE: pipeline/forms.py
================================================
"""Support for referencing Pipeline packages in forms and widgets."""

from django.contrib.staticfiles.storage import staticfiles_storage
from django.utils.functional import cached_property

from .collector import default_collector
from .conf import settings
from .packager import Packager


class PipelineFormMediaProperty:
    """A property that converts Pipeline packages to lists of files.

    This is used behind the scenes for any Media classes that subclass
    :py:class:`PipelineFormMedia`. When accessed, it converts any Pipeline
    packages into lists of media files and returns or forwards on lookups to
    that list.
    """

    def __init__(self, get_media_files_func, media_cls, extra_files):
        """Initialize the property.

        Args:
            get_media_files_func (callable):
                The function to call to generate the media files.

            media_cls (type):
                The Media class owning the property.

            extra_files (object):
                Files listed in the original ``css`` or ``js`` attribute on
                the Media class.
        """
        self._get_media_files_func = get_media_files_func
        self._media_cls = media_cls
        self._extra_files = extra_files

    @cached_property
    def _media_files(self):
        """The media files represented by the property."""
        return self._get_media_files_func(self._media_cls, self._extra_files)

    def __get__(self, *args, **kwargs):
        """Return the media files when accessed as an attribute.

        This is called when accessing the attribute directly through the
        Media class (for example, ``Media.css``). It returns the media files
        directly.

        Args:
            *args (tuple, unused):
                Unused positional arguments.

            **kwargs (dict, unused):
                Unused keyword arguments.

        Returns:
            object:
            The list or dictionary containing the media files definition.
        """
        return self._media_files

    def __getattr__(self, attr_name):
        """Return an attribute on the media files definition.

        This is called when accessing an attribute that doesn't otherwise
        exist in the property's dictionary. The call is forwarded onto the
        media files definition.

        Args:
            attr_name (unicode):
                The attribute name.

        Returns:
            object:
            The attribute value.

        Raises:
            AttributeError:
                An attribute with this name could not be found.
        """
        return getattr(self._media_files, attr_name)

    def __iter__(self):
        """Iterate through the media files definition.

        This is called when attempting to iterate over this property. It
        iterates over the media files definition instead.

        Yields:
            object:
            Each entry in the media files definition.
        """
        return iter(self._media_files)


class PipelineFormMediaMetaClass(type):
    """Metaclass for the PipelineFormMedia class.

    This is responsible for converting CSS/JavaScript packages defined in
    Pipeline into lists of files to include on a page. It handles access to the
    :py:attr:`css` and :py:attr:`js` attributes on the class, generating a
    list of files to return based on the Pipelined packages and individual
    files listed in the :py:attr:`css`/:py:attr:`css_packages` or
    :py:attr:`js`/:py:attr:`js_packages` attributes.
    """

    def __new__(cls, name, bases, attrs):
        """Construct the class.

        Args:
            name (bytes):
                The name of the class.

            bases (tuple):
                The base classes for the class.

            attrs (dict):
                The attributes going into the class.

        Returns:
            type:
            The new class.
        """
        new_class = super().__new__(cls, name, bases, attrs)

        # If we define any packages, we'll need to use our special
        # PipelineFormMediaProperty class. We use this instead of intercepting
        # in __getattribute__ because Django does not access them through
        # normal property access. Instead, grabs the Media class's __dict__
        # and accesses them from there. By using these special properties, we
        # can handle direct access (Media.css) and dictionary-based access
        # (Media.__dict__['css']).
        if "css_packages" in attrs:
            new_class.css = PipelineFormMediaProperty(
                cls._get_css_files, new_class, attrs.get("css") or {}
            )

        if "js_packages" in attrs:
            new_class.js = PipelineFormMediaProperty(
                cls._get_js_files, new_class, attrs.get("js") or []
            )

        return new_class

    def _get_css_files(cls, extra_files):
        """Return all CSS files from the Media class.

        Args:
            extra_files (dict):
                The contents of the Media class's original :py:attr:`css`
                attribute, if one was provided.

        Returns:
            dict:
            The CSS media types and files to return for the :py:attr:`css`
            attribute.
        """
        packager = Packager()
        css_packages = getattr(cls, "css_packages", {})

        return {
            media_target: cls._get_media_files(
                packager=packager,
                media_packages=media_packages,
                media_type="css",
                extra_files=extra_files.get(media_target, []),
            )
            for media_target, media_packages in css_packages.items()
        }

    def _get_js_files(cls, extra_files):
        """Return all JavaScript files from the Media class.

        Args:
            extra_files (list):
                The contents of the Media class's original :py:attr:`js`
                attribute, if one was provided.

        Returns:
            list:
            The JavaScript files to return for the :py:attr:`js` attribute.
        """
        return cls._get_media_files(
            packager=Packager(),
            media_packages=getattr(cls, "js_packages", {}),
            media_type="js",
            extra_files=extra_files,
        )

    def _get_media_files(cls, packager, media_packages, media_type, extra_files):
        """Return source or output media files for a list of packages.

        This will go through the media files belonging to the provided list
        of packages referenced in a Media class and return the output files
        (if Pipeline is enabled) or the source files (if not enabled).

        Args:
            packager (pipeline.packager.Packager):
                The packager responsible for media compilation for this type
                of package.

            media_packages (list of unicode):
                The list of media packages referenced in Media to compile or
                return.

            extra_files (list of unicode):
                The list of extra files to include in the result. This would
                be the list stored in the Media class's original :py:attr:`css`
                or :py:attr:`js` attributes.

        Returns:
            list:
            The list of media files for the given packages.
        """
        source_files = list(extra_files)

        if not settings.PIPELINE_ENABLED and settings.PIPELINE_COLLECTOR_ENABLED:
            default_collector.collect()

        for media_package in media_packages:
            package = packager.package_for(media_type, media_package)

            if settings.PIPELINE_ENABLED:
                source_files.append(staticfiles_storage.url(package.output_filename))
            else:
                source_files += packager.compile(package.paths)

        return source_files


class PipelineFormMedia(metaclass=PipelineFormMediaMetaClass):
    """Base class for form or widget Media classes that use Pipeline packages.

    Forms or widgets that need custom CSS or JavaScript media on a page can
    define a standard :py:class:`Media` class that subclasses this class,
    listing the CSS or JavaScript packages in :py:attr:`css_packages` and
    :py:attr:`js_packages` attributes. These are formatted the same as the
    standard :py:attr:`css` and :py:attr:`js` attributes, but reference
    Pipeline package names instead of individual source files.

    If Pipeline is enabled, these will expand to the output files for the
    packages. Otherwise, these will expand to the list of source files for the
    packages.

    Subclasses can also continue to define :py:attr:`css` and :py:attr:`js`
    attributes, which will be returned along with the other output/source
    files.

    Example:

        from django import forms
        from pipeline.forms import PipelineFormMedia


        class MyForm(forms.Media):
            ...

            class Media(PipelineFormMedia):
                css_packages = {
                    'all': ('my-form-styles-package',
                            'other-form-styles-package'),
                    'print': ('my-form-print-styles-package',),
                }

                js_packages = ('my-form-scripts-package',)
                js = ('some-file.js',)
    """


================================================
FILE: pipeline/glob.py
================================================
import fnmatch
import os
import re

from django.contrib.staticfiles.storage import staticfiles_storage

__all__ = ["glob", "iglob"]


def glob(pathname):
    """Return a list of paths matching a pathname pattern.

    The pattern may contain simple shell-style wildcards a la fnmatch.

    """
    return sorted(list(iglob(pathname)))


def iglob(pathname):
    """Return an iterator which yields the paths matching a pathname pattern.

    The pattern may contain simple shell-style wildcards a la fnmatch.

    """
    if not has_magic(pathname):
        yield pathname
        return
    dirname, basename = os.path.split(pathname)
    if not dirname:
        for name in glob1(dirname, basename):
            yield name
        return
    if has_magic(dirname):
        dirs = iglob(dirname)
    else:
        dirs = [dirname]
    if has_magic(basename):
        glob_in_dir = glob1
    else:
        glob_in_dir = glob0
    for dirname in dirs:
        for name in glob_in_dir(dirname, basename):
            yield os.path.join(dirname, name)


# These 2 helper functions non-recursively glob inside a literal directory.
# They return a list of basenames. `glob1` accepts a pattern while `glob0`
# takes a literal basename (so it only has to check for its existence).


def glob1(dirname, pattern):
    try:
        directories, files = staticfiles_storage.listdir(dirname)
        names = directories + files
    except Exception:
        # We are not sure that dirname is a real directory
        # and storage implementations are really exotic.
        return []
    if pattern[0] != ".":
        names = [x for x in names if x[0] != "."]
    return fnmatch.filter(names, pattern)


def glob0(dirname, basename):
    if staticfiles_storage.exists(os.path.join(dirname, basename)):
        return [basename]
    return []


magic_check = re.compile("[*?[]")


def has_magic(s):
    return magic_check.search(s) is not None


================================================
FILE: pipeline/jinja2/__init__.py
================================================
from django.contrib.staticfiles.storage import staticfiles_storage
from jinja2 import TemplateSyntaxError, nodes
from jinja2.ext import Extension

from ..packager import PackageNotFound
from ..templatetags.pipeline import PipelineMixin
from ..utils import guess_type


class PipelineExtension(PipelineMixin, Extension):
    tags = {"stylesheet", "javascript"}

    def parse(self, parser):
        tag = next(parser.stream)

        package_name = parser.parse_expression()
        if not package_name:
            raise TemplateSyntaxError("Bad package name", tag.lineno)

        args = [package_name]
        if tag.value == "stylesheet":
            return nodes.CallBlock(
                self.call_method("package_css", args), [], [], []
            ).set_lineno(tag.lineno)

        if tag.value == "javascript":
            return nodes.CallBlock(
                self.call_method("package_js", args), [], [], []
            ).set_lineno(tag.lineno)

        return []

    def package_css(self, package_name, *args, **kwargs):
        try:
            package = self.package_for(package_name, "css")
        except PackageNotFound:
            # fail silently, do not return anything if an invalid group is specified
            return ""
        return self.render_compressed(package, package_name, "css")

    def render_css(self, package, path):
        template_name = package.template_name or "pipeline/css.jinja"
        context = package.extra_context
        context.update(
            {"type": guess_type(path, "text/css"), "url": staticfiles_storage.url(path)}
        )
        template = self.environment.get_template(template_name)
        return template.render(context)

    def render_individual_css(self, package, paths, **kwargs):
        tags = [self.render_css(package, path) for path in paths]
        return "\n".join(tags)

    def package_js(self, package_name, *args, **kwargs):
        try:
            package = self.package_for(package_name, "js")
        except PackageNotFound:
            # fail silently, do not return anything if an invalid group is specified
            return ""
        return self.render_compressed(package, package_name, "js")

    def render_js(self, package, path):
        template_name = package.template_name or "pipeline/js.jinja"
        context = package.extra_context
        context.update(
            {
                "type": guess_type(path, "text/javascript"),
                "url": staticfiles_storage.url(path),
            }
        )
        template = self.environment.get_template(template_name)
        return template.render(context)

    def render_inline(self, package, js):
        context = package.extra_context
        context.update({"source": js})
        template = self.environment.get_template("pipeline/inline_js.jinja")
        return template.render(context)

    def render_individual_js(self, package, paths, templates=None):
        tags = [self.render_js(package, js) for js in paths]
        if templates:
            tags.append(self.render_inline(package, templates))
        return "\n".join(tags)


================================================
FILE: pipeline/jinja2/pipeline/css.jinja
================================================
<link href="{{ url }}" rel="stylesheet" type="{{ type }}"{% if media %} media="{{ media }}"{% endif %}{% if charset %} charset="{{ charset }}"{% endif %} />


================================================
FILE: pipeline/jinja2/pipeline/inline_js.jinja
================================================
<script{% if async %} async{% endif %}{% if defer %} defer{% endif %} type="text/javascript">
  {{ source|safe }}
</script>


================================================
FILE: pipeline/jinja2/pipeline/js.jinja
================================================
<script{% if async %} async{% endif %}{% if defer %} defer{% endif %} type="{{ type }}" src="{{ url }}" charset="utf-8"></script>


================================================
FILE: pipeline/middleware.py
================================================
from django.core.exceptions import MiddlewareNotUsed
from django.utils.deprecation import MiddlewareMixin
from django.utils.encoding import DjangoUnicodeDecodeError
from django.utils.html import strip_spaces_between_tags as minify_html

from pipeline.conf import settings


class MinifyHTMLMiddleware(MiddlewareMixin):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if not settings.PIPELINE_ENABLED:
            raise MiddlewareNotUsed

    def process_response(self, request, response):
        if (
            response.has_header("Content-Type")
            and "text/html" in response["Content-Type"]
        ):
            try:
                response.content = minify_html(response.content.decode("utf-8").strip())
                response["Content-Length"] = str(len(response.content))
            except DjangoUnicodeDecodeError:
                pass
        return response


================================================
FILE: pipeline/packager.py
================================================
from django.contrib.staticfiles.finders import find, get_finders
from django.contrib.staticfiles.storage import staticfiles_storage
from django.core.files.base import ContentFile
from django.utils.encoding import smart_bytes

from pipeline.compilers import Compiler
from pipeline.compressors import Compressor
from pipeline.conf import settings
from pipeline.exceptions import PackageNotFound
from pipeline.glob import glob
from pipeline.signals import css_compressed, js_compressed


class Package:
    def __init__(self, config):
        self.config = config
        self._sources = []

    @property
    def sources(self):
        if not self._sources:
            paths = []
            for pattern in self.config.get("source_filenames", []):
                for path in glob(pattern):
                    if path not in paths and find(path):
                        paths.append(str(path))
            self._sources = paths
        return self._sources

    @property
    def paths(self):
        return [
            path for path in self.sources if not path.endswith(settings.TEMPLATE_EXT)
        ]

    @property
    def templates(self):
        return [path for path in self.sources if path.endswith(settings.TEMPLATE_EXT)]

    @property
    def output_filename(self):
        return self.config.get("output_filename")

    @property
    def extra_context(self):
        return self.config.get("extra_context", {})

    @property
    def template_name(self):
        return self.config.get("template_name")

    @property
    def variant(self):
        return self.config.get("variant")

    @property
    def manifest(self):
        return self.config.get("manifest", True)

    @property
    def compiler_options(self):
        return self.config.get("compiler_options", {})


class Packager:
    def __init__(
        self,
        storage=None,
        verbose=False,
        css_packages=None,
        js_packages=None,
    ):
        if storage is None:
            storage = staticfiles_storage
        self.storage = storage
        self.verbose = verbose
        self.compressor = Compressor(storage=storage, verbose=verbose)
        self.compiler = Compiler(storage=storage, verbose=verbose)
        if css_packages is None:
            css_packages = settings.STYLESHEETS
        if js_packages is None:
            js_packages = settings.JAVASCRIPT
        self.packages = {
            "css": self.create_packages(css_packages),
            "js": self.create_packages(js_packages),
        }

    def package_for(self, kind, package_name):
        try:
            return self.packages[kind][package_name]
        except KeyError:
            raise PackageNotFound(
                "No corresponding package for {} package name : {}".format(
                    kind, package_name
                )
            )

    def individual_url(self, filename):
        return self.storage.url(filename)

    def pack_stylesheets(self, package, **kwargs):
        return self.pack(
            package,
            self.compressor.compress_css,
            css_compressed,
            output_filename=package.output_filename,
            variant=package.variant,
            **kwargs,
        )

    def compile(self, paths, compiler_options={}, force=False):
        paths = self.compiler.compile(
            paths,
            compiler_options=compiler_options,
            force=force,
        )
        for path in paths:
            if not self.storage.exists(path):
                if self.verbose:
                    e = (
                        "Compiled file '%s' cannot be "
                        "found with packager's storage. Locating it."
                    )
                    print(e % path)

                source_storage = self.find_source_storage(path)
                if source_storage is not None:
                    with source_storage.open(path) as source_file:
                        if self.verbose:
                            print(f"Saving: {path}")
                        self.storage.save(path, source_file)
                else:
                    raise OSError(f"File does not exist: {path}")
        return paths

    def pack(self, package, compress, signal, **kwargs):
        output_filename = package.output_filename
        if self.verbose:
            print(f"Saving: {output_filename}")
        paths = self.compile(
            package.paths,
            compiler_options=package.compiler_options,
            force=True,
        )
        content = compress(paths, **kwargs)
        self.save_file(output_filename, content)
        signal.send(sender=self, package=package, **kwargs)
        return output_filename

    def pack_javascripts(self, package, **kwargs):
        return self.pack(
            package,
            self.compressor.compress_js,
            js_compressed,
            output_filename=package.output_filename,
            templates=package.templates,
            **kwargs,
        )

    def pack_templates(self, package):
        return self.compressor.compile_templates(package.templates)

    def save_file(self, path, content):
        return self.storage.save(path, ContentFile(smart_bytes(content)))

    def find_source_storage(self, path):
        for finder in get_finders():
            for short_path, storage in finder.list(""):
                if short_path == path:
                    if self.verbose:
                        print(f"Found storage: {str(self.storage)}")
                    return storage
        return None

    def create_packages(self, config):
        packages = {}
        if not config:
            return packages
        for name in config:
            packages[name] = Package(config[name])
        return packages


================================================
FILE: pipeline/signals.py
================================================
from django.dispatch import Signal

css_compressed = Signal()
js_compressed = Signal()


================================================
FILE: pipeline/storage.py
================================================
import gzip
from io import BytesIO

from django import get_version as django_version
from django.contrib.staticfiles.storage import (
    ManifestStaticFilesStorage,
    StaticFilesStorage,
)
from django.contrib.staticfiles.utils import matches_patterns
from django.core.files.base import File

_CACHED_STATIC_FILES_STORAGE_AVAILABLE = django_version() < "3.1"

if _CACHED_STATIC_FILES_STORAGE_AVAILABLE:
    from django.contrib.staticfiles.storage import CachedStaticFilesStorage


class PipelineMixin:
    packing = True

    def post_process(self, paths, dry_run=False, **options):
        if dry_run:
            return

        from pipeline.packager import Packager  # noqa: PLC0415

        packager = Packager(storage=self)
        for package_name in packager.packages["css"]:
            package = packager.package_for("css", package_name)
            output_file = package.output_filename
            if self.packing:
                packager.pack_stylesheets(package)
            paths[output_file] = (self, output_file)
            yield output_file, output_file, True
        for package_name in packager.packages["js"]:
            package = packager.package_for("js", package_name)
            output_file = package.output_filename
            if self.packing:
                packager.pack_javascripts(package)
            paths[output_file] = (self, output_file)
            yield output_file, output_file, True

        super_class = super()
        if hasattr(super_class, "post_process"):
            yield from super_class.post_process(paths.copy(), dry_run, **options)

    def get_available_name(self, name, max_length=None):
        if self.exists(name):
            self.delete(name)
        return name


class GZIPMixin:
    gzip_patterns = ("*.css", "*.js")

    def _compress(self, original_file):
        content = BytesIO()
        gzip_file = gzip.GzipFile(mode="wb", fileobj=content)
        gzip_file.write(original_file.read())
        gzip_file.close()
        content.seek(0)
        return File(content)

    def post_process(self, paths, dry_run=False, **options):
        super_class = super()
        if hasattr(super_class, "post_process"):
            for name, hashed_name, processed in super_class.post_process(
                paths.copy(), dry_run, **options
            ):
                if hashed_name != name:
                    paths[hashed_name] = (self, hashed_name)
                yield name, hashed_name, processed

        if dry_run:
            return

        for path in paths:
            if path:
                if not matches_patterns(path, self.gzip_patterns):
                    continue
                original_file = self.open(path)
                gzipped_path = f"{path}.gz"
                if self.exists(gzipped_path):
                    self.delete(gzipped_path)
                gzipped_file = self._compress(original_file)
                gzipped_path = self.save(gzipped_path, gzipped_file)
                yield gzipped_path, gzipped_path, True


class NonPackagingMixin:
    packing = False


class PipelineStorage(PipelineMixin, StaticFilesStorage):
    pass


class NonPackagingPipelineStorage(NonPackagingMixin, PipelineStorage):
    pass


if _CACHED_STATIC_FILES_STORAGE_AVAILABLE:

    class PipelineCachedStorage(PipelineMixin, CachedStaticFilesStorage):
        # Deprecated since Django 2.2
        # Removed in Django 3.1
        pass

    class NonPackagingPipelineCachedStorage(NonPackagingMixin, PipelineCachedStorage):
        # Deprecated since Django 2.2
        # Removed in Django 3.1
        pass


class PipelineManifestStorage(PipelineMixin, ManifestStaticFilesStorage):
    pass


class NonPackagingPipelineManifestStorage(
    NonPackagingMixin, ManifestStaticFilesStorage
):
    pass


================================================
FILE: pipeline/templates/pipeline/compile_error.html
================================================
<div id="django-pipeline-error-{{package_name}}" class="django-pipeline-error"
     style="display: none; border: 2px #DD0000 solid; margin: 1em; padding: 1em; background: white; color: black;">
 <h1>Error compiling {{package_type}} package "{{package_name}}"</h1>
 <p><strong>Command:</strong></p>
 <pre style="white-space: pre-wrap;">{{command}}</pre>
 <p><strong>Errors:</strong></p>
 <pre style="white-space: pre-wrap;">{{errors}}</pre>
</div>

<script>
document.addEventListener('readystatechange', function() {
    var el,
        container;

    if (document.readyState !== 'interactive') {
        return;
    }

    el = document.getElementById('django-pipeline-error-{{package_name}}');
    container = document.getElementById('django-pipeline-errors');

    if (!container) {
        container = document.createElement('div');
        container.id = 'django-pipeline-errors';
        document.body.insertBefore(container, document.body.firstChild);
    }

    container.appendChild(el);
    el.style.display = 'block';
});
</script>


================================================
FILE: pipeline/templates/pipeline/css.html
================================================
<link href="{{ url }}" rel="stylesheet" type="{{ type }}" media="{{ media|default:"all" }}"{% if title %} title="{{ title }}"{% endif %}{% if charset %} charset="{{ charset }}"{% endif %} />

================================================
FILE: pipeline/templates/pipeline/css.jinja
================================================
<link href="{{ url }}" rel="stylesheet" type="{{ type }}"{% if media %} media="{{ media }}"{% endif %}{% if charset %} charset="{{ charset }}"{% endif %} />


================================================
FILE: pipeline/templates/pipeline/inline_js.html
================================================
<script{% if async %} async{% endif %}{% if defer %} defer{% endif %} type="text/javascript">
  {{ source|safe }}
</script>

================================================
FILE: pipeline/templates/pipeline/inline_js.jinja
================================================
<script{% if async %} async{% endif %}{% if defer %} defer{% endif %} type="text/javascript">
  {{ source|safe }}
</script>


================================================
FILE: pipeline/templates/pipeline/js.html
================================================
<script{% if async %} async{% endif %}{% if defer %} defer{% endif %} type="{{ type }}" src="{{ url }}" charset="utf-8"></script>

================================================
FILE: pipeline/templates/pipeline/js.jinja
================================================
<script{% if async %} async{% endif %}{% if defer %} defer{% endif %} type="{{ type }}" src="{{ url }}" charset="utf-8"></script>


================================================
FILE: pipeline/templatetags/__init__.py
================================================


================================================
FILE: pipeline/templatetags/pipeline.py
================================================
import logging
import re
import subprocess

from django import template
from django.contrib.staticfiles.storage import staticfiles_storage
from django.template.base import VariableDoesNotExist
from django.template.loader import render_to_string
from django.utils.safestring import mark_safe

from ..collector import default_collector
from ..conf import settings
from ..exceptions import CompilerError
from ..packager import PackageNotFound, Packager
from ..utils import guess_type

logger = logging.getLogger(__name__)

register = template.Library()


class PipelineMixin:
    request = None
    _request_var = None

    @property
    def request_var(self):
        if not self._request_var:
            self._request_var = template.Variable("request")
        return self._request_var

    def package_for(self, package_name, package_type):
        package = {
            "js": getattr(settings, "JAVASCRIPT", {}).get(package_name, {}),
            "css": getattr(settings, "STYLESHEETS", {}).get(package_name, {}),
        }[package_type]

        if package:
            package = {package_name: package}

        packager = {
            "js": Packager(css_packages={}, js_packages=package),
            "css": Packager(css_packages=package, js_packages={}),
        }[package_type]

        return packager.package_for(package_type, package_name)

    def render(self, context):
        try:
            self.request = self.request_var.resolve(context)
        except VariableDoesNotExist:
            pass

    def render_compressed(self, package, package_name, package_type):
        """Render HTML for the package.

        If ``PIPELINE_ENABLED`` is ``True``, this will render the package's
        output file (using :py:meth:`render_compressed_output`). Otherwise,
        this will render the package's source files (using
        :py:meth:`render_compressed_sources`).

        Subclasses can override this method to provide custom behavior for
        determining what to render.
        """
        if settings.PIPELINE_ENABLED:
            return self.render_compressed_output(package, package_name, package_type)
        else:
            return self.render_compressed_sources(package, package_name, package_type)

    def render_compressed_output(self, package, package_name, package_type):
        """Render HTML for using the package's output file.

        Subclasses can override this method to provide custom behavior for
        rendering the output file.
        """
        method = getattr(self, f"render_{package_type}")

        return method(package, package.output_filename)

    def render_compressed_sources(self, package, package_name, package_type):
        """Render HTML for using the package's list of source files.

        Each source file will first be collected, if
        ``PIPELINE_COLLECTOR_ENABLED`` is ``True``.

        If there are any errors compiling any of the source files, an
        ``SHOW_ERRORS_INLINE`` is ``True``, those errors will be shown at
        the top of the page.

        Subclasses can override this method to provide custom behavior for
        rendering the source files.
        """
        if settings.PIPELINE_COLLECTOR_ENABLED:
            default_collector.collect(self.request)

        packager = Packager()
        method = getattr(self, f"render_individual_{package_type}")

        try:
            paths = packager.compile(package.paths)
        except CompilerError as e:
            if settings.SHOW_ERRORS_INLINE:
                method = getattr(self, f"render_error_{package_type}")
                return method(package_name, e)
            else:
                raise

        templates = packager.pack_templates(package)

        return method(package, paths, templates=templates)

    def render_error(self, package_type, package_name, e):
        # Remove any ANSI escape sequences in the output.
        error_output = re.sub(
            re.compile(r"(?:\x1B[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]"),
            "",
            e.error_output,
        )

        return render_to_string(
            "pipeline/compile_error.html",
            {
                "package_type": package_type,
                "package_name": package_name,
                "command": subprocess.list2cmdline(e.command),
                "errors": error_output,
            },
        )


class StylesheetNode(PipelineMixin, template.Node):
    def __init__(self, name):
        self.name = name

    def render(self, context):
        super().render(context)
        package_name = template.Variable(self.name).resolve(context)

        try:
            package = self.package_for(package_name, "css")
        except PackageNotFound:
            w = "Package %r is unknown. Check PIPELINE['STYLESHEETS'] in your settings."
            logger.warning(w, package_name)
            # fail silently, do not return anything if an invalid group is specified
            return ""
        return self.render_compressed(package, package_name, "css")

    def render_css(self, package, path):
        template_name = package.template_name or "pipeline/css.html"
        context = package.extra_context
        context.update(
            {
                "type": guess_type(path, "text/css"),
                "url": mark_safe(staticfiles_storage.url(path)),
            }
        )
        return render_to_string(template_name, context)

    def render_individual_css(self, package, paths, **kwargs):
        tags = [self.render_css(package, path) for path in paths]
        return "\n".join(tags)

    def render_error_css(self, package_name, e):
        return super().render_error("CSS", package_name, e)


class JavascriptNode(PipelineMixin, template.Node):
    def __init__(self, name):
        self.name = name

    def render(self, context):
        super().render(context)
        package_name = template.Variable(self.name).resolve(context)

        try:
            package = self.package_for(package_name, "js")
        except PackageNotFound:
            w = "Package %r is unknown. Check PIPELINE['JAVASCRIPT'] in your settings."
            logger.warning(w, package_name)
            # fail silently, do not return anything if an invalid group is specified
            return ""
        return self.render_compressed(package, package_name, "js")

    def render_js(self, package, path):
        template_name = package.template_name or "pipeline/js.html"
        context = package.extra_context
        context.update(
            {
                "type": guess_type(path, "text/javascript"),
                "url": mark_safe(staticfiles_storage.url(path)),
            }
        )
        return render_to_string(template_name, context)

    def render_inline(self, package, js):
        context = package.extra_context
        context.update({"source": js})
        return render_to_string("pipeline/inline_js.html", context)

    def render_individual_js(self, package, paths, templates=None):
        tags = [self.render_js(package, js) for js in paths]
        if templates:
            tags.append(self.render_inline(package, templates))
        return "\n".join(tags)

    def render_error_js(self, package_name, e):
        return super().render_error("JavaScript", package_name, e)


@register.tag
def stylesheet(parser, token):
    try:
        tag_name, name = token.split_contents()
    except ValueError:
        e = (
            "%r requires exactly one argument: the name "
            "of a group in the PIPELINE.STYLESHEETS setting"
        )
        raise template.TemplateSyntaxError(e % token.split_contents()[0])
    return StylesheetNode(name)


@register.tag
def javascript(parser, token):
    try:
        tag_name, name = token.split_contents()
    except ValueError:
        e = (
            "%r requires exactly one argument: the name "
            "of a group in the PIPELINE.JAVASVRIPT setting"
        )
        raise template.TemplateSyntaxError(e % token.split_contents()[0])
    return JavascriptNode(name)


================================================
FILE: pipeline/utils.py
================================================
try:
    import fcntl
except ImportError:
    # windows
    fcntl = None

import importlib
import mimetypes
import os
import posixpath
import sys
from urllib.parse import quote

from django.utils.encoding import smart_str

from pipeline.conf import settings


def to_class(class_str):
    if not class_str:
        return None

    module_bits = class_str.split(".")
    module_path, class_name = ".".join(module_bits[:-1]), module_bits[-1]
    module = importlib.import_module(module_path)
    return getattr(module, class_name, None)


def filepath_to_uri(path):
    if path is None:
        return path
    return quote(smart_str(path).replace("\\", "/"), safe="/~!*()'#?")


def guess_type(path, default=None):
    for type, ext in settings.MIMETYPES:
        mimetypes.add_type(type, ext)
    mimetype, _ = mimetypes.guess_type(path)
    if not mimetype:
        return default
    return smart_str(mimetype)


def relpath(path, start=posixpath.curdir):
    """Return a relative version of a path"""
    if not path:
        raise ValueError("no path specified")

    start_list = posixpath.abspath(start).split(posixpath.sep)
    path_list = posixpath.abspath(path).split(posixpath.sep)

    # Work out how much of the filepath is shared by start and path.
    i = len(posixpath.commonprefix([start_list, path_list]))

    rel_list = [posixpath.pardir] * (len(start_list) - i) + path_list[i:]
    if not rel_list:
        return posixpath.curdir
    return posixpath.join(*rel_list)


def set_std_streams_blocking():
    """
    Set stdout and stderr to be blocking.

    This is called after Popen.communicate() to revert stdout and stderr back
    to be blocking (the default) in the event that the process to which they
    were passed manipulated one or both file descriptors to be non-blocking.
    """
    if not fcntl:
        return
    for f in (sys.__stdout__, sys.__stderr__):
        fileno = f.fileno()
        flags = fcntl.fcntl(fileno, fcntl.F_GETFL)
        fcntl.fcntl(fileno, fcntl.F_SETFL, flags & ~os.O_NONBLOCK)


================================================
FILE: pipeline/views.py
================================================
from django.conf import settings as django_settings
from django.core.exceptions import ImproperlyConfigured
from django.views.static import serve

from .collector import default_collector
from .conf import settings


def serve_static(request, path, insecure=False, **kwargs):
    """Collect and serve static files.

    This view serves up static files, much like Django's
    :py:func:`~django.views.static.serve` view, with the addition that it
    collects static files first (if enabled). This allows images, fonts, and
    other assets to be served up without first loading a page using the
    ``{% javascript %}`` or ``{% stylesheet %}`` template tags.

    You can use this view by adding the following to any :file:`urls.py`::

        urlpatterns += static('static/', view='pipeline.views.serve_static')
    """
    # Follow the same logic Django uses for determining access to the
    # static-serving view.
    if not django_settings.DEBUG and not insecure:
        raise ImproperlyConfigured(
            "The staticfiles view can only be used in "
            "debug mode or if the --insecure "
            "option of 'runserver' is used"
        )

    if not settings.PIPELINE_ENABLED and settings.PIPELINE_COLLECTOR_ENABLED:
        # Collect only the requested file, in order to serve the result as
        # fast as possible. This won't interfere with the template tags in any
        # way, as those will still cause Django to collect all media.
        default_collector.collect(request, files=[path])

    return serve(request, path, document_root=django_settings.STATIC_ROOT, **kwargs)


================================================
FILE: pyproject.toml
================================================
[build-system]
requires = ["setuptools>=64", "setuptools_scm[toml]>=8"]
build-backend = "setuptools.build_meta"

[project]
name = "django-pipeline"
requires-python = ">=3.9"
version = "4.1.0"
description = "Pipeline is an asset packaging library for Django."
readme = "README.rst"
authors = [{ "name" = "Timothée Peignier", "email" = "timothee.peignier@tryphon.org" }]
license = { text = "MIT" }
classifiers = [
    "Development Status :: 5 - Production/Stable",
    "Environment :: Web Environment",
    "Framework :: Django",
    "Framework :: Django :: 4.0",
    "Framework :: Django :: 4.1",
    "Framework :: Django :: 4.2",
    "Framework :: Django :: 5.0",
    "Framework :: Django :: 5.1",
    "Framework :: Django :: 5.2",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: MIT License",
    "Operating System :: OS Independent",
    "Programming Language :: Python",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3 :: Only",
    "Programming Language :: Python :: 3.9",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "Programming Language :: Python :: 3.13",
    "Programming Language :: Python :: Implementation :: PyPy",
    "Topic :: Utilities",
    "Topic :: Software Development :: Libraries :: Python Modules",
    "Topic :: Internet :: WWW/HTTP",
    "Topic :: Internet :: WWW/HTTP :: Dynamic Content",
]
keywords = [
    "django",
    "pipeline",
    "asset",
    "compiling",
    "concatenation",
    "compression",
    "packaging",
]
dependencies = [
    # indirect dependencies
    "setuptools",
    "wheel",
]

[project.optional-dependencies]
testing = [
    "coveralls",
    "tox",
    "wheel",
    "django",
]

[project.urls]
homepage = "https://github.com/jazzband/django-pipeline/"
documentation = "https://django-pipeline.readthedocs.io/"
repository = "https://github.com/jazzband/django-pipeline"
changelog = "https://github.com/jazzband/django-pipeline/blob/master/HISTORY.rst"

[tool.setuptools]
include-package-data = true

[tool.setuptools.packages.find]
exclude = ["tests", "tests.tests"]

[tool.setuptools_scm]
local_scheme = "dirty-tag"

[tool.black]
line-length = 88
target-version = ["py39"]

[tool.isort]
profile = "black"


================================================
FILE: tests/__init__.py
================================================


================================================
FILE: tests/assets/compilers/coffee/expected.js
================================================
(function() {
  var cube, square;

  square = function(x) {
    return x * x;
  };

  cube = function(x) {
    return square(x) * x;
  };

}).call(this);


================================================
FILE: tests/assets/compilers/coffee/input.coffee
================================================
square = (x) -> x * x
cube   = (x) -> square(x) * x


================================================
FILE: tests/assets/compilers/es6/expected.js
================================================
"use strict";

// Expression bodies
var odds = evens.map(function (v) {
  return v + 1;
});
var nums = evens.map(function (v, i) {
  return v + i;
});

// Statement bodies
nums.forEach(function (v) {
  if (v % 5 === 0) fives.push(v);
});

// Lexical this
var bob = {
  _name: "Bob",
  _friends: [],
  printFriends: function printFriends() {
    var _this = this;

    this._friends.forEach(function (f) {
      return console.log(_this._name + " knows " + f);
    });
  }
};


================================================
FILE: tests/assets/compilers/es6/input.es6
================================================
// Expression bodies
var odds = evens.map(v => v + 1);
var nums = evens.map((v, i) => v + i);

// Statement bodies
nums.forEach(v => {
  if (v % 5 === 0)
    fives.push(v);
});

// Lexical this
var bob = {
  _name: "Bob",
  _friends: [],
  printFriends() {
    this._friends.forEach(f =>
      console.log(this._name + " knows " + f));
  }
};


================================================
FILE: tests/assets/compilers/less/expected.css
================================================
.a {
  width: 1px;
}


================================================
FILE: tests/assets/compilers/less/input.less
================================================
@a: 1;

.a {
    width: (@a + 0px);
}


================================================
FILE: tests/assets/compilers/livescript/expected.js
================================================
(function(){
  var times;
  times = function(x, y){
    return x * y;
  };
}).call(this);


================================================
FILE: tests/assets/compilers/livescript/input.ls
================================================
times = (x, y) ->
  x * y


================================================
FILE: tests/assets/compilers/scss/expected.css
================================================
.a .b {
  display: none;
}

.c .d {
  display: block;
}

/*# sourceMappingURL=input.css.map */


================================================
FILE: tests/assets/compilers/scss/input.scss
================================================
.a {
    .b {
        display: none;
    }
}
.c {
    .d {
        display: block;
    }
}


================================================
FILE: tests/assets/compilers/stylus/expected.css
================================================
.a {
  color: #000;
}


================================================
FILE: tests/assets/compilers/stylus/input.styl
================================================
.a
    color: black

================================================
FILE: tests/assets/compilers/typescript/expected.js
================================================
function getName(u) {
    return "".concat(u.firstName, " ").concat(u.lastName);
}
var userName = getName({ firstName: "Django", lastName: "Pipeline" });


================================================
FILE: tests/assets/compilers/typescript/input.ts
================================================
type FullName = string;

interface User {
    firstName: string;
    lastName: string;
}


function getName(u: User): FullName {
    return `${u.firstName} ${u.lastName}`;
}

let userName: FullName = getName({firstName: "Django", lastName: "Pipeline"});


================================================
FILE: tests/assets/compressors/closure.js
================================================
(function(){(function(){window.concat=function(){console.log(arguments)}})();(function(){window.cat=function(){console.log("hello world")}})()}).call(this);


================================================
FILE: tests/assets/compressors/csshtmljsminify.css
================================================
@charset "utf-8";.concat{display:none}.concatenate{display:block}

================================================
FILE: tests/assets/compressors/csshtmljsminify.js
================================================
(function(){(function(){window.concat=function(){console.log(arguments);}}());(function(){window.cat=function(){console.log("hello world");}}());}).call(this);

================================================
FILE: tests/assets/compressors/cssmin.css
================================================
.concat{display:none}.concatenate{display:block}

================================================
FILE: tests/assets/compressors/csstidy.css
================================================
.concat{display:none;}.concatenate{display:block;}

================================================
FILE: tests/assets/compressors/jsmin.js
================================================
(function(){(function(){window.concat=function(){console.log(arguments);}}());(function(){window.cat=function(){console.log("hello world");}}());}).call(this);

================================================
FILE: tests/assets/compressors/slimit.js
================================================
(function(){(function(){window.concat=function(){console.log(arguments);};}());(function(){window.cat=function(){console.log("hello world");};}());}).call(this);

================================================
FILE: tests/assets/compressors/terser.js
================================================
(function(){window.concat=function(){console.log(arguments)},window.cat=function(){console.log("hello world")}}).call(this);


================================================
FILE: tests/assets/compressors/uglifyjs.js
================================================
(function(){(function(){window.concat=function(){console.log(arguments)}})();(function(){window.cat=function(){console.log("hello world")}})()}).call(this);


================================================
FILE: tests/assets/compressors/yuglify.css
================================================
.concat{display:none}.concatenate{display:block}


================================================
FILE: tests/assets/compressors/yuglify.js
================================================
(function(){!function(){window.concat=function(){console.log(arguments)}}(),function(){window.cat=function(){console.log("hello world")}}()}).call(this);


================================================
FILE: tests/assets/compressors/yui.css
================================================
.concat{display:none}.concatenate{display:block}

================================================
FILE: tests/assets/compressors/yui.js
================================================
(function(){(function(){window.concat=function(){console.log(arguments)}}());(function(){window.cat=function(){console.log("hello world")}}())}).call(this);

================================================
FILE: tests/assets/css/first.css
================================================
.concat {
  display: none;
}


================================================
FILE: tests/assets/css/nested/nested.css
================================================
.data-url {
  background-image: url(data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2212px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2012%2014%22%20style%3D%22enable-background%3Anew%200%200%2012%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M11%2C6V5c0-2.762-2.239-5-5-5S1%2C2.238%2C1%2C5v1H0v8h12V6H11z%20M6.5%2C9.847V12h-1V9.847C5.207%2C9.673%2C5%2C9.366%2C5%2C9%20c0-0.553%2C0.448-1%2C1-1s1%2C0.447%2C1%2C1C7%2C9.366%2C6.793%2C9.673%2C6.5%2C9.847z%20M9%2C6H3V5c0-1.657%2C1.343-3%2C3-3s3%2C1.343%2C3%2C3V6z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E);
}
.data-url-quoted {
  background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2212px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2012%2014%22%20style%3D%22enable-background%3Anew%200%200%2012%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M11%2C6V5c0-2.762-2.239-5-5-5S1%2C2.238%2C1%2C5v1H0v8h12V6H11z%20M6.5%2C9.847V12h-1V9.847C5.207%2C9.673%2C5%2C9.366%2C5%2C9%20c0-0.553%2C0.448-1%2C1-1s1%2C0.447%2C1%2C1C7%2C9.366%2C6.793%2C9.673%2C6.5%2C9.847z%20M9%2C6H3V5c0-1.657%2C1.343-3%2C3-3s3%2C1.343%2C3%2C3V6z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
}


================================================
FILE: tests/assets/css/second.css
================================================
.concatenate {
  display: block;
}


================================================
FILE: tests/assets/css/sourcemap.css
================================================
div {
  display: inline;
}

span {
  display: block;
}


//#  sourceMappingURL=sourcemap1.css.map

//@ sourceMappingURL=sourcemap2.css.map  

/*#  sourceMappingURL=sourcemap3.css.map */

/*@ sourceMappingURL=sourcemap4.css.map  */

//#  sourceURL=sourcemap5.css.map

//@ sourceURL=sourcemap6.css.map  

/*#  sourceURL=sourcemap7.css.map */

/*@ sourceURL=sourcemap8.css.map  */


================================================
FILE: tests/assets/css/unicode.css
================================================
.some_class {
  // Some unicode
  content: "áéíóú";
}


================================================
FILE: tests/assets/css/urls.css
================================================
.embedded-url-svg {
  background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath      stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 8h24M4 16h24M4 24h24'/%3E%     3C/svg%3E");
}
@font-face {
  font-family: 'Pipeline';
  src: url('../fonts/pipeline.eot');
  src: url('../fonts/pipeline.eot?#iefix') format('embedded-opentype');
  src: local('☺'), url('../fonts/pipeline.woff') format('woff'), url('../fonts/pipeline.ttf') format('truetype'), url('../fonts/pipeline.svg#IyfZbseF') format('svg');
  font-weight: normal;
  font-style: normal;
}
.relative-url {
  background-image: url(../images/sprite-buttons.png);
}
.relative-url-querystring {
  background-image: url(../images/sprite-buttons.png?v=1.0#foo=bar);
}
.absolute-url {
  background-image: url(/images/sprite-buttons.png);
}
.absolute-full-url {
  background-image: url(http://localhost/images/sprite-buttons.png);
}
.no-protocol-url {
  background-image: url(//images/sprite-buttons.png);
}
.anchor-tag-url {
  background-image: url(#image-gradient);
}
@font-face{src:url(../fonts/pipeline.eot);src:url(../fonts/pipeline.eot?#iefix) format('embedded-opentype'),url(../fonts/pipeline.woff) format('woff'),url(../fonts/pipeline.ttf) format('truetype');}


================================================
FILE: tests/assets/js/application.js
================================================
function test() {
  alert('this is a test');
}


================================================
FILE: tests/assets/js/dummy.coffee
================================================
square = (x) -> x * x
cube   = (x) -> square(x) * x


================================================
FILE: tests/assets/js/first.js
================================================
(function() {
  window.concat = function() {
    console.log(arguments);
  }
}()) // No semicolon


================================================
FILE: tests/assets/js/second.js
================================================
(function() {
  window.cat = function() {
    console.log("hello world");
  }
}());


================================================
FILE: tests/assets/js/sourcemap.js
================================================
const abc = 123;


//#  sourceMappingURL=sourcemap1.js.map

//@ sourceMappingURL=sourcemap2.js.map  

/*#  sourceMappingURL=sourcemap3.js.map */

/*@ sourceMappingURL=sourcemap4.js.map  */

//#  sourceURL=sourcemap5.js.map

//@ sourceURL=sourcemap6.js.map  

/*#  sourceURL=sourcemap7.js.map */

/*@ sourceURL=sourcemap8.js.map  */


================================================
FILE: tests/assets/templates/photo/detail.jst
================================================
<div class="photo">
 <img src="<%= src %>" />
 <div class="caption">
  <%= caption %> by <%= author %>
 </div>
</div>

================================================
FILE: tests/assets/templates/photo/list.jst
================================================
<div class="photo">
 <img src="<%= src %>" />
 <div class="caption">
  <%= caption %>
 </div>
</div>

================================================
FILE: tests/assets/templates/video/detail.jst
================================================
<div class="video">
 <video src="<%= src %>" />
 <div class="caption">
  <%= description %>
 </div>
</div>

================================================
FILE: tests/models.py
================================================


================================================
FILE: tests/settings.py
================================================
import glob
import os
import shutil


def local_path(path):
    return os.path.join(os.path.dirname(__file__), path)


DATABASES = {
    "default": {"ENGINE": "django.db.backends.sqlite3", "TEST_NAME": ":memory:"}
}

DEBUG = False

SITE_ID = 1

INSTALLED_APPS = [
    "django.contrib.contenttypes",
    "django.contrib.messages",
    "django.contrib.sites",
    "django.contrib.sessions",
    "django.contrib.staticfiles",
    "django.contrib.auth",
    "django.contrib.admin",
    "pipeline",
    "tests.tests",
]


ROOT_URLCONF = "tests.urls"

MIDDLEWARE = [
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
]

MEDIA_URL = "/media/"

MEDIA_ROOT = local_path("media")

STATICFILES_STORAGE = "pipeline.storage.PipelineStorage"
STATIC_ROOT = local_path("static/")
STATIC_URL = "/static/"
STATICFILES_DIRS = (("pipeline", local_path("assets/")),)
STATICFILES_FINDERS = (
    "django.contrib.staticfiles.finders.FileSystemFinder",
    "django.contrib.staticfiles.finders.AppDirectoriesFinder",
    "pipeline.finders.PipelineFinder",
)

SECRET_KEY = "django-pipeline"

PIPELINE = {
    "PIPELINE_ENABLED": True,
    "JS_COMPRESSOR": None,
    "CSS_COMPRESSOR": None,
    "STYLESHEETS": {
        "screen": {
            "source_filenames": (
                "pipeline/css/first.css",
                "pipeline/css/second.css",
                "pipeline/css/urls.css",
            ),
            "output_filename": "screen.css",
        },
        "screen_media": {
            "source_filenames": (
                "pipeline/css/first.css",
                "pipeline/css/second.css",
                "pipeline/css/urls.css",
            ),
            "output_filename": "screen_media.css",
            "extra_context": {
                "media": "screen and (min-width:500px)",
            },
        },
        "screen_title": {
            "source_filenames": (
                "pipeline/css/first.css",
                "pipeline/css/second.css",
                "pipeline/css/urls.css",
            ),
            "output_filename": "screen_title.css",
            "extra_context": {
                "title": "Default Style",
            },
        },
    },
    "JAVASCRIPT": {
        "scripts": {
            "source_filenames": (
                "pipeline/js/first.js",
                "pipeline/js/second.js",
                "pipeline/js/application.js",
                "pipeline/templates/**/*.jst",
            ),
            "output_filename": "scripts.js",
        },
        "scripts_async": {
            "source_filenames": (
                "pipeline/js/first.js",
                "pipeline/js/second.js",
                "pipeline/js/application.js",
                "pipeline/templates/**/*.jst",
            ),
            "output_filename": "scripts_async.js",
            "extra_context": {
                "async": True,
            },
        },
        "scripts_defer": {
            "source_filenames": (
                "pipeline/js/first.js",
                "pipeline/js/second.js",
                "pipeline/js/application.js",
                "pipeline/templates/**/*.jst",
            ),
            "output_filename": "scripts_defer.js",
            "extra_context": {
                "defer": True,
            },
        },
        "scripts_async_defer": {
            "source_filenames": (
                "pipeline/js/first.js",
                "pipeline/js/second.js",
                "pipeline/js/application.js",
                "pipeline/templates/**/*.jst",
            ),
            "output_filename": "scripts_async_defer.js",
            "extra_context": {
                "async": True,
                "defer": True,
            },
        },
    },
}

NODE_MODULES_PATH = local_path("../node_modules")
NODE_BIN_PATH = os.path.join(NODE_MODULES_PATH, ".bin")
NODE_EXE_PATH = shutil.which("node")
JAVA_EXE_PATH = shutil.which("java")
CSSTIDY_EXE_PATH = shutil.which("csstidy")
HAS_NODE = bool(NODE_EXE_PATH)
HAS_JAVA = bool(JAVA_EXE_PATH)
HAS_CSSTIDY = bool(CSSTIDY_EXE_PATH)

if HAS_NODE:

    def node_exe_path(command):
        exe_ext = ".cmd" if os.name == "nt" else ""
        return os.path.join(NODE_BIN_PATH, "{}{}".format(command, exe_ext))

    PIPELINE.update(
        {
            "SASS_BINARY": node_exe_path("sass"),
            "COFFEE_SCRIPT_BINARY": node_exe_path("coffee"),
            "COFFEE_SCRIPT_ARGUMENTS": ["--no-header"],
            "LESS_BINARY": node_exe_path("lessc"),
            "BABEL_BINARY": node_exe_path("babel"),
            "BABEL_ARGUMENTS": ["--presets", "es2015"],
            "STYLUS_BINARY": node_exe_path("stylus"),
            "LIVE_SCRIPT_BINARY": node_exe_path("lsc"),
            "LIVE_SCRIPT_ARGUMENTS": ["--no-header"],
            "YUGLIFY_BINARY": node_exe_path("yuglify"),
            "UGLIFYJS_BINARY": node_exe_path("uglifyjs"),
            "TERSER_BINARY": node_exe_path("terser"),
            "CSSMIN_BINARY": node_exe_path("cssmin"),
            "TYPE_SCRIPT_BINARY": node_exe_path("tsc"),
        }
    )

if HAS_NODE and HAS_JAVA:
    PIPELINE.update(
        {
            "CLOSURE_BINARY": [
                JAVA_EXE_PATH,
                "-jar",
                os.path.join(
                    NODE_MODULES_PATH,
                    "google-closure-compiler-java",
                    "compiler.jar",
                ),
            ],
            "YUI_BINARY": [
                JAVA_EXE_PATH,
                "-jar",
                glob.glob(
                    os.path.join(NODE_MODULES_PATH, "yuicompressor", "build", "*.jar")
                )[0],
            ],
        }
    )

if HAS_CSSTIDY:
    PIPELINE.update({"CSSTIDY_BINARY": CSSTIDY_EXE_PATH})

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "APP_DIRS": True,
        "DIRS": [local_path("templates")],
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ]
        },
    },
    {
        "BACKEND": "django.template.backends.jinja2.Jinja2",
        "APP_DIRS": True,
        "DIRS": [local_path("templates")],
        "OPTIONS": {"extensions": ["pipeline.jinja2.PipelineExtension"]},
    },
]

LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
        },
    },
    "loggers": {
        "pipeline.templatetags.pipeline": {
            "handlers": ["console"],
            "level": "ERROR",
        },
    },
}


================================================
FILE: tests/templates/empty.html
================================================
 

================================================
FILE: tests/templates/index.html
================================================
{% load pipeline %}
<html>
 <head>
  <meta http-equiv="Content-type" content="text/html; charset=utf-8">
  <title>Pipeline</title>
  {% stylesheet 'screen' %}
  {% javascript 'scripts' %}
 </head>
</html>


================================================
FILE: tests/tests/__init__.py
================================================
import os
import sys

if sys.platform.startswith("win"):
    os.environ.setdefault("NUMBER_OF_PROCESSORS", "1")


from .test_collector import *  # noqa
from .test_compiler import *  # noqa
from .test_compressor import *  # noqa
from .test_glob import *  # noqa
from .test_middleware import *  # noqa
from .test_packager import *  # noqa
from .test_storage import *  # noqa
from .test_template import *  # noqa
from .test_utils import *  # noqa
from .test_views import *  # noqa


================================================
FILE: tests/tests/models.py
================================================


================================================
FILE: tests/tests/test_collector.py
================================================
import os

from django.contrib.staticfiles import finders
from django.core.files.storage import FileSystemStorage
from django.test import TestCase

from pipeline.collector import default_collector
from pipeline.finders import PipelineFinder


def local_path(path):
    return os.path.abspath(os.path.join(os.path.dirname(__file__), "..", path))


class CollectorTest(TestCase):
    def tearDown(self):
        super().tearDown()

        default_collector.clear()

    def test_collect(self):
        self.assertEqual(
            set(default_collector.collect()), set(self._get_collectable_files())
        )

    def test_collect_with_files(self):
        self.assertEqual(
            set(
                default_collector.collect(
                    files=[
                        "pipeline/js/first.js",
                        "pipeline/js/second.js",
                    ]
                )
            ),
            {
                "pipeline/js/first.js",
                "pipeline/js/second.js",
            },
     
Download .txt
gitextract_vhr1bgzi/

├── .flake8
├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── release.yml
│       └── test.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .readthedocs.yaml
├── AUTHORS
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.rst
├── HISTORY.rst
├── LICENSE
├── MANIFEST.in
├── README.rst
├── docs/
│   ├── Makefile
│   ├── compilers.rst
│   ├── compressors.rst
│   ├── conf.py
│   ├── configuration.rst
│   ├── index.rst
│   ├── installation.rst
│   ├── make.bat
│   ├── signals.rst
│   ├── storages.rst
│   ├── templates.rst
│   ├── usage.rst
│   └── using.rst
├── package.json
├── pipeline/
│   ├── __init__.py
│   ├── collector.py
│   ├── compilers/
│   │   ├── __init__.py
│   │   ├── coffee.py
│   │   ├── es6.py
│   │   ├── less.py
│   │   ├── livescript.py
│   │   ├── sass.py
│   │   ├── stylus.py
│   │   └── typescript.py
│   ├── compressors/
│   │   ├── __init__.py
│   │   ├── closure.py
│   │   ├── csshtmljsminify.py
│   │   ├── cssmin.py
│   │   ├── csstidy.py
│   │   ├── jsmin.py
│   │   ├── terser.py
│   │   ├── uglifyjs.py
│   │   ├── yuglify.py
│   │   └── yui.py
│   ├── conf.py
│   ├── exceptions.py
│   ├── finders.py
│   ├── forms.py
│   ├── glob.py
│   ├── jinja2/
│   │   ├── __init__.py
│   │   └── pipeline/
│   │       ├── css.jinja
│   │       ├── inline_js.jinja
│   │       └── js.jinja
│   ├── middleware.py
│   ├── packager.py
│   ├── signals.py
│   ├── storage.py
│   ├── templates/
│   │   └── pipeline/
│   │       ├── compile_error.html
│   │       ├── css.html
│   │       ├── css.jinja
│   │       ├── inline_js.html
│   │       ├── inline_js.jinja
│   │       ├── js.html
│   │       └── js.jinja
│   ├── templatetags/
│   │   ├── __init__.py
│   │   └── pipeline.py
│   ├── utils.py
│   └── views.py
├── pyproject.toml
├── tests/
│   ├── __init__.py
│   ├── assets/
│   │   ├── compilers/
│   │   │   ├── coffee/
│   │   │   │   ├── expected.js
│   │   │   │   └── input.coffee
│   │   │   ├── es6/
│   │   │   │   ├── expected.js
│   │   │   │   └── input.es6
│   │   │   ├── less/
│   │   │   │   ├── expected.css
│   │   │   │   └── input.less
│   │   │   ├── livescript/
│   │   │   │   ├── expected.js
│   │   │   │   └── input.ls
│   │   │   ├── scss/
│   │   │   │   ├── expected.css
│   │   │   │   └── input.scss
│   │   │   ├── stylus/
│   │   │   │   ├── expected.css
│   │   │   │   └── input.styl
│   │   │   └── typescript/
│   │   │       ├── expected.js
│   │   │       └── input.ts
│   │   ├── compressors/
│   │   │   ├── closure.js
│   │   │   ├── csshtmljsminify.css
│   │   │   ├── csshtmljsminify.js
│   │   │   ├── cssmin.css
│   │   │   ├── csstidy.css
│   │   │   ├── jsmin.js
│   │   │   ├── slimit.js
│   │   │   ├── terser.js
│   │   │   ├── uglifyjs.js
│   │   │   ├── yuglify.css
│   │   │   ├── yuglify.js
│   │   │   ├── yui.css
│   │   │   └── yui.js
│   │   ├── css/
│   │   │   ├── first.css
│   │   │   ├── nested/
│   │   │   │   └── nested.css
│   │   │   ├── second.css
│   │   │   ├── sourcemap.css
│   │   │   ├── unicode.css
│   │   │   └── urls.css
│   │   ├── js/
│   │   │   ├── application.js
│   │   │   ├── dummy.coffee
│   │   │   ├── first.js
│   │   │   ├── second.js
│   │   │   └── sourcemap.js
│   │   └── templates/
│   │       ├── photo/
│   │       │   ├── detail.jst
│   │       │   └── list.jst
│   │       └── video/
│   │           └── detail.jst
│   ├── models.py
│   ├── settings.py
│   ├── templates/
│   │   ├── empty.html
│   │   └── index.html
│   ├── tests/
│   │   ├── __init__.py
│   │   ├── models.py
│   │   ├── test_collector.py
│   │   ├── test_compiler.py
│   │   ├── test_compressor.py
│   │   ├── test_conf.py
│   │   ├── test_forms.py
│   │   ├── test_glob.py
│   │   ├── test_middleware.py
│   │   ├── test_packager.py
│   │   ├── test_storage.py
│   │   ├── test_template.py
│   │   ├── test_utils.py
│   │   └── test_views.py
│   ├── urls.py
│   ├── utils.py
│   └── views.py
└── tox.ini
Download .txt
SYMBOL INDEX (428 symbols across 49 files)

FILE: pipeline/__init__.py
  function get_version (line 13) | def get_version(x):

FILE: pipeline/collector.py
  class Collector (line 10) | class Collector:
    method __init__ (line 13) | def __init__(self, storage=None):
    method _get_modified_time (line 18) | def _get_modified_time(self, storage, prefixed_path):
    method clear (line 23) | def clear(self, path=""):
    method collect (line 31) | def collect(self, request=None, files=[]):
    method copy_file (line 58) | def copy_file(self, path, prefixed_path, source_storage):
    method delete_file (line 66) | def delete_file(self, path, prefixed_path, source_storage):

FILE: pipeline/compilers/__init__.py
  class Compiler (line 16) | class Compiler:
    method __init__ (line 17) | def __init__(self, storage=None, verbose=False):
    method compilers (line 24) | def compilers(self):
    method compile (line 27) | def compile(self, paths, compiler_options={}, force=False):
  class CompilerBase (line 63) | class CompilerBase:
    method __init__ (line 64) | def __init__(self, verbose, storage):
    method match_file (line 68) | def match_file(self, filename):
    method compile_file (line 71) | def compile_file(self, infile, outfile, outdated=False, force=False):
    method save_file (line 74) | def save_file(self, path, content):
    method read_file (line 77) | def read_file(self, path):
    method output_path (line 83) | def output_path(self, path, extension):
    method is_outdated (line 87) | def is_outdated(self, infile, outfile):
  class SubProcessCompiler (line 97) | class SubProcessCompiler(CompilerBase):
    method execute_command (line 98) | def execute_command(self, command, cwd=None, stdout_captured=None):

FILE: pipeline/compilers/coffee.py
  class CoffeeScriptCompiler (line 5) | class CoffeeScriptCompiler(SubProcessCompiler):
    method match_file (line 8) | def match_file(self, path):
    method compile_file (line 11) | def compile_file(self, infile, outfile, outdated=False, force=False):

FILE: pipeline/compilers/es6.py
  class ES6Compiler (line 5) | class ES6Compiler(SubProcessCompiler):
    method match_file (line 8) | def match_file(self, path):
    method compile_file (line 11) | def compile_file(self, infile, outfile, outdated=False, force=False):

FILE: pipeline/compilers/less.py
  class LessCompiler (line 7) | class LessCompiler(SubProcessCompiler):
    method match_file (line 10) | def match_file(self, filename):
    method compile_file (line 13) | def compile_file(self, infile, outfile, outdated=False, force=False):

FILE: pipeline/compilers/livescript.py
  class LiveScriptCompiler (line 5) | class LiveScriptCompiler(SubProcessCompiler):
    method match_file (line 8) | def match_file(self, path):
    method compile_file (line 11) | def compile_file(self, infile, outfile, outdated=False, force=False):

FILE: pipeline/compilers/sass.py
  class SASSCompiler (line 7) | class SASSCompiler(SubProcessCompiler):
    method match_file (line 10) | def match_file(self, filename):
    method compile_file (line 13) | def compile_file(self, infile, outfile, outdated=False, force=False):

FILE: pipeline/compilers/stylus.py
  class StylusCompiler (line 7) | class StylusCompiler(SubProcessCompiler):
    method match_file (line 10) | def match_file(self, filename):
    method compile_file (line 13) | def compile_file(self, infile, outfile, outdated=False, force=False):

FILE: pipeline/compilers/typescript.py
  class TypeScriptCompiler (line 5) | class TypeScriptCompiler(SubProcessCompiler):
    method match_file (line 8) | def match_file(self, path):
    method compile_file (line 11) | def compile_file(self, infile, outfile, outdated=False, force=False):

FILE: pipeline/compressors/__init__.py
  class Compressor (line 90) | class Compressor:
    method __init__ (line 93) | def __init__(self, storage=None, verbose=False):
    method js_compressor (line 100) | def js_compressor(self):
    method css_compressor (line 104) | def css_compressor(self):
    method compress_js (line 107) | def compress_js(
    method compress_css (line 140) | def compress_css(self, paths, output_filename, variant=None, **kwargs):
    method compile_templates (line 159) | def compile_templates(self, paths):
    method base_path (line 187) | def base_path(self, paths):
    method template_name (line 194) | def template_name(self, path, base):
    method concatenate_and_rewrite (line 209) | def concatenate_and_rewrite(self, paths, output_filename, variant=None):
    method concatenate (line 226) | def concatenate(
    method construct_asset_path (line 314) | def construct_asset_path(
    method embeddable (line 328) | def embeddable(self, path, variant):
    method with_data_uri (line 347) | def with_data_uri(self, css):
    method encoded_content (line 356) | def encoded_content(self, path):
    method mime_type (line 364) | def mime_type(self, path):
    method absolute_path (line 369) | def absolute_path(self, path, start):
    method relative_path (line 380) | def relative_path(self, absolute_path, output_filename):
    method read_bytes (line 388) | def read_bytes(self, path):
    method read_text (line 395) | def read_text(self, path):
  class CompressorBase (line 400) | class CompressorBase:
    method __init__ (line 401) | def __init__(self, verbose):
    method filter_css (line 404) | def filter_css(self, css):
    method filter_js (line 407) | def filter_js(self, js):
  class SubProcessCompressor (line 411) | class SubProcessCompressor(CompressorBase):
    method execute_command (line 412) | def execute_command(self, command, content):
  class NoopCompressor (line 437) | class NoopCompressor(CompressorBase):
    method compress_js (line 438) | def compress_js(self, js):
    method compress_css (line 441) | def compress_css(self, css):

FILE: pipeline/compressors/closure.py
  class ClosureCompressor (line 5) | class ClosureCompressor(SubProcessCompressor):
    method compress_js (line 6) | def compress_js(self, js):

FILE: pipeline/compressors/csshtmljsminify.py
  class CssHtmlJsMinifyCompressor (line 4) | class CssHtmlJsMinifyCompressor(CompressorBase):
    method compress_css (line 10) | def compress_css(self, css):
    method compress_js (line 15) | def compress_js(self, js):

FILE: pipeline/compressors/cssmin.py
  class CSSMinCompressor (line 5) | class CSSMinCompressor(SubProcessCompressor):
    method compress_css (line 6) | def compress_css(self, css):

FILE: pipeline/compressors/csstidy.py
  class CSSTidyCompressor (line 7) | class CSSTidyCompressor(SubProcessCompressor):
    method compress_css (line 8) | def compress_css(self, css):

FILE: pipeline/compressors/jsmin.py
  class JSMinCompressor (line 4) | class JSMinCompressor(CompressorBase):
    method compress_js (line 10) | def compress_js(self, js):

FILE: pipeline/compressors/terser.py
  class TerserCompressor (line 5) | class TerserCompressor(SubProcessCompressor):
    method compress_js (line 6) | def compress_js(self, js):

FILE: pipeline/compressors/uglifyjs.py
  class UglifyJSCompressor (line 5) | class UglifyJSCompressor(SubProcessCompressor):
    method compress_js (line 6) | def compress_js(self, js):

FILE: pipeline/compressors/yuglify.py
  class YuglifyCompressor (line 5) | class YuglifyCompressor(SubProcessCompressor):
    method compress_common (line 6) | def compress_common(self, content, compress_type, arguments):
    method compress_js (line 10) | def compress_js(self, js):
    method compress_css (line 13) | def compress_css(self, css):

FILE: pipeline/compressors/yui.py
  class YUICompressor (line 5) | class YUICompressor(SubProcessCompressor):
    method compress_common (line 6) | def compress_common(self, content, compress_type, arguments):
    method compress_js (line 10) | def compress_js(self, js):
    method compress_css (line 13) | def compress_css(self, css):

FILE: pipeline/conf.py
  class PipelineSettings (line 69) | class PipelineSettings(MutableMapping):
    method __init__ (line 74) | def __init__(self, wrapped_settings):
    method __getitem__ (line 78) | def __getitem__(self, key):
    method __setitem__ (line 86) | def __setitem__(self, key, value):
    method __delitem__ (line 89) | def __delitem__(self, key):
    method __iter__ (line 92) | def __iter__(self):
    method __len__ (line 95) | def __len__(self):
    method __getattr__ (line 98) | def __getattr__(self, name):
  function reload_settings (line 106) | def reload_settings(**kwargs):

FILE: pipeline/exceptions.py
  class PipelineException (line 1) | class PipelineException(Exception):
  class PackageNotFound (line 5) | class PackageNotFound(PipelineException):
  class CompilerError (line 9) | class CompilerError(PipelineException):
    method __init__ (line 10) | def __init__(self, msg, command=None, error_output=None):
  class CompressorError (line 17) | class CompressorError(PipelineException):

FILE: pipeline/finders.py
  class PipelineFinder (line 18) | class PipelineFinder(BaseStorageFinder):
    method find (line 21) | def find(self, path, **kwargs):
    method list (line 27) | def list(self, ignore_patterns):
  class ManifestFinder (line 31) | class ManifestFinder(BaseFinder):
    method find (line 32) | def find(self, path, **kwargs):
    method list (line 45) | def list(self, *args):
  class CachedFileFinder (line 49) | class CachedFileFinder(BaseFinder):
    method find (line 50) | def find(self, path, **kwargs):
    method list (line 61) | def list(self, *args):
  class PatternFilterMixin (line 65) | class PatternFilterMixin:
    method get_ignored_patterns (line 68) | def get_ignored_patterns(self):
    method list (line 71) | def list(self, ignore_patterns):
  class AppDirectoriesFinder (line 77) | class AppDirectoriesFinder(PatternFilterMixin, DjangoAppDirectoriesFinder):
  class FileSystemFinder (line 95) | class FileSystemFinder(PatternFilterMixin, DjangoFileSystemFinder):

FILE: pipeline/forms.py
  class PipelineFormMediaProperty (line 11) | class PipelineFormMediaProperty:
    method __init__ (line 20) | def __init__(self, get_media_files_func, media_cls, extra_files):
    method _media_files (line 39) | def _media_files(self):
    method __get__ (line 43) | def __get__(self, *args, **kwargs):
    method __getattr__ (line 63) | def __getattr__(self, attr_name):
    method __iter__ (line 84) | def __iter__(self):
  class PipelineFormMediaMetaClass (line 97) | class PipelineFormMediaMetaClass(type):
    method __new__ (line 108) | def __new__(cls, name, bases, attrs):
    method _get_css_files (line 146) | def _get_css_files(cls, extra_files):
    method _get_js_files (line 172) | def _get_js_files(cls, extra_files):
    method _get_media_files (line 191) | def _get_media_files(cls, packager, media_packages, media_type, extra_...
  class PipelineFormMedia (line 232) | class PipelineFormMedia(metaclass=PipelineFormMediaMetaClass):

FILE: pipeline/glob.py
  function glob (line 10) | def glob(pathname):
  function iglob (line 19) | def iglob(pathname):
  function glob1 (line 51) | def glob1(dirname, pattern):
  function glob0 (line 64) | def glob0(dirname, basename):
  function has_magic (line 73) | def has_magic(s):

FILE: pipeline/jinja2/__init__.py
  class PipelineExtension (line 10) | class PipelineExtension(PipelineMixin, Extension):
    method parse (line 13) | def parse(self, parser):
    method package_css (line 33) | def package_css(self, package_name, *args, **kwargs):
    method render_css (line 41) | def render_css(self, package, path):
    method render_individual_css (line 50) | def render_individual_css(self, package, paths, **kwargs):
    method package_js (line 54) | def package_js(self, package_name, *args, **kwargs):
    method render_js (line 62) | def render_js(self, package, path):
    method render_inline (line 74) | def render_inline(self, package, js):
    method render_individual_js (line 80) | def render_individual_js(self, package, paths, templates=None):

FILE: pipeline/middleware.py
  class MinifyHTMLMiddleware (line 9) | class MinifyHTMLMiddleware(MiddlewareMixin):
    method __init__ (line 10) | def __init__(self, *args, **kwargs):
    method process_response (line 15) | def process_response(self, request, response):

FILE: pipeline/packager.py
  class Package (line 14) | class Package:
    method __init__ (line 15) | def __init__(self, config):
    method sources (line 20) | def sources(self):
    method paths (line 31) | def paths(self):
    method templates (line 37) | def templates(self):
    method output_filename (line 41) | def output_filename(self):
    method extra_context (line 45) | def extra_context(self):
    method template_name (line 49) | def template_name(self):
    method variant (line 53) | def variant(self):
    method manifest (line 57) | def manifest(self):
    method compiler_options (line 61) | def compiler_options(self):
  class Packager (line 65) | class Packager:
    method __init__ (line 66) | def __init__(
    method package_for (line 88) | def package_for(self, kind, package_name):
    method individual_url (line 98) | def individual_url(self, filename):
    method pack_stylesheets (line 101) | def pack_stylesheets(self, package, **kwargs):
    method compile (line 111) | def compile(self, paths, compiler_options={}, force=False):
    method pack (line 136) | def pack(self, package, compress, signal, **kwargs):
    method pack_javascripts (line 150) | def pack_javascripts(self, package, **kwargs):
    method pack_templates (line 160) | def pack_templates(self, package):
    method save_file (line 163) | def save_file(self, path, content):
    method find_source_storage (line 166) | def find_source_storage(self, path):
    method create_packages (line 175) | def create_packages(self, config):

FILE: pipeline/storage.py
  class PipelineMixin (line 18) | class PipelineMixin:
    method post_process (line 21) | def post_process(self, paths, dry_run=False, **options):
    method get_available_name (line 47) | def get_available_name(self, name, max_length=None):
  class GZIPMixin (line 53) | class GZIPMixin:
    method _compress (line 56) | def _compress(self, original_file):
    method post_process (line 64) | def post_process(self, paths, dry_run=False, **options):
  class NonPackagingMixin (line 90) | class NonPackagingMixin:
  class PipelineStorage (line 94) | class PipelineStorage(PipelineMixin, StaticFilesStorage):
  class NonPackagingPipelineStorage (line 98) | class NonPackagingPipelineStorage(NonPackagingMixin, PipelineStorage):
  class PipelineCachedStorage (line 104) | class PipelineCachedStorage(PipelineMixin, CachedStaticFilesStorage):
  class NonPackagingPipelineCachedStorage (line 109) | class NonPackagingPipelineCachedStorage(NonPackagingMixin, PipelineCache...
  class PipelineManifestStorage (line 115) | class PipelineManifestStorage(PipelineMixin, ManifestStaticFilesStorage):
  class NonPackagingPipelineManifestStorage (line 119) | class NonPackagingPipelineManifestStorage(

FILE: pipeline/templatetags/pipeline.py
  class PipelineMixin (line 22) | class PipelineMixin:
    method request_var (line 27) | def request_var(self):
    method package_for (line 32) | def package_for(self, package_name, package_type):
    method render (line 48) | def render(self, context):
    method render_compressed (line 54) | def render_compressed(self, package, package_name, package_type):
    method render_compressed_output (line 70) | def render_compressed_output(self, package, package_name, package_type):
    method render_compressed_sources (line 80) | def render_compressed_sources(self, package, package_name, package_type):
    method render_error (line 112) | def render_error(self, package_type, package_name, e):
  class StylesheetNode (line 131) | class StylesheetNode(PipelineMixin, template.Node):
    method __init__ (line 132) | def __init__(self, name):
    method render (line 135) | def render(self, context):
    method render_css (line 148) | def render_css(self, package, path):
    method render_individual_css (line 159) | def render_individual_css(self, package, paths, **kwargs):
    method render_error_css (line 163) | def render_error_css(self, package_name, e):
  class JavascriptNode (line 167) | class JavascriptNode(PipelineMixin, template.Node):
    method __init__ (line 168) | def __init__(self, name):
    method render (line 171) | def render(self, context):
    method render_js (line 184) | def render_js(self, package, path):
    method render_inline (line 195) | def render_inline(self, package, js):
    method render_individual_js (line 200) | def render_individual_js(self, package, paths, templates=None):
    method render_error_js (line 206) | def render_error_js(self, package_name, e):
  function stylesheet (line 211) | def stylesheet(parser, token):
  function javascript (line 224) | def javascript(parser, token):

FILE: pipeline/utils.py
  function to_class (line 19) | def to_class(class_str):
  function filepath_to_uri (line 29) | def filepath_to_uri(path):
  function guess_type (line 35) | def guess_type(path, default=None):
  function relpath (line 44) | def relpath(path, start=posixpath.curdir):
  function set_std_streams_blocking (line 61) | def set_std_streams_blocking():

FILE: pipeline/views.py
  function serve_static (line 9) | def serve_static(request, path, insecure=False, **kwargs):

FILE: tests/assets/compilers/typescript/expected.js
  function getName (line 1) | function getName(u) {

FILE: tests/assets/compilers/typescript/input.ts
  type FullName (line 1) | type FullName = string;
  type User (line 3) | interface User {
  function getName (line 9) | function getName(u: User): FullName {

FILE: tests/assets/js/application.js
  function test (line 1) | function test() {

FILE: tests/settings.py
  function local_path (line 6) | def local_path(path):
  function node_exe_path (line 154) | def node_exe_path(command):

FILE: tests/tests/test_collector.py
  function local_path (line 11) | def local_path(path):
  class CollectorTest (line 15) | class CollectorTest(TestCase):
    method tearDown (line 16) | def tearDown(self):
    method test_collect (line 21) | def test_collect(self):
    method test_collect_with_files (line 26) | def test_collect_with_files(self):
    method test_delete_file_with_modified (line 42) | def test_delete_file_with_modified(self):
    method test_delete_file_with_unmodified (line 58) | def test_delete_file_with_unmodified(self):
    method _get_collectable_files (line 69) | def _get_collectable_files(self):

FILE: tests/tests/test_compiler.py
  class FailingCompiler (line 16) | class FailingCompiler(SubProcessCompiler):
    method match_file (line 19) | def match_file(self, path):
    method compile_file (line 22) | def compile_file(self, infile, outfile, outdated=False, force=False):
  class InvalidCompiler (line 32) | class InvalidCompiler(SubProcessCompiler):
    method match_file (line 35) | def match_file(self, path):
    method compile_file (line 38) | def compile_file(self, infile, outfile, outdated=False, force=False):
  class CompilerWithEmptyFirstArg (line 47) | class CompilerWithEmptyFirstArg(SubProcessCompiler):
    method match_file (line 50) | def match_file(self, path):
    method compile_file (line 53) | def compile_file(self, infile, outfile, outdated=False, force=False):
  class CopyingCompiler (line 61) | class CopyingCompiler(SubProcessCompiler):
    method match_file (line 64) | def match_file(self, path):
    method compile_file (line 67) | def compile_file(self, infile, outfile, outdated=False, force=False):
  class LineNumberingCompiler (line 72) | class LineNumberingCompiler(SubProcessCompiler):
    method match_file (line 75) | def match_file(self, path):
    method compile_file (line 78) | def compile_file(self, infile, outfile, outdated=False, force=False):
  class DummyCompiler (line 87) | class DummyCompiler(CompilerBase):
    method match_file (line 90) | def match_file(self, path):
    method compile_file (line 93) | def compile_file(self, infile, outfile, outdated=False, force=False):
  class DummyCompilerTest (line 98) | class DummyCompilerTest(TestCase):
    method setUp (line 99) | def setUp(self):
    method test_output_path (line 103) | def test_output_path(self):
    method test_compilers_class (line 112) | def test_compilers_class(self):
    method test_compile (line 116) | def test_compile(self):
    method tearDown (line 128) | def tearDown(self):
  class CompilerStdoutTest (line 134) | class CompilerStdoutTest(TestCase):
    method setUp (line 135) | def setUp(self):
    method test_output_path (line 139) | def test_output_path(self):
    method test_compile (line 148) | def test_compile(self):
    method tearDown (line 152) | def tearDown(self):
  class CompilerSelfWriterTest (line 158) | class CompilerSelfWriterTest(TestCase):
    method setUp (line 159) | def setUp(self):
    method test_output_path (line 163) | def test_output_path(self):
    method test_compile (line 172) | def test_compile(self):
    method tearDown (line 177) | def tearDown(self):
  class CompilerWithEmptyFirstArgTest (line 182) | class CompilerWithEmptyFirstArgTest(TestCase):
    method setUp (line 183) | def setUp(self):
    method test_compile (line 187) | def test_compile(self):
    method tearDown (line 192) | def tearDown(self):
  class InvalidCompilerTest (line 197) | class InvalidCompilerTest(TestCase):
    method setUp (line 198) | def setUp(self):
    method test_compile (line 202) | def test_compile(self):
    method tearDown (line 216) | def tearDown(self):
  class FailingCompilerTest (line 222) | class FailingCompilerTest(TestCase):
    method setUp (line 223) | def setUp(self):
    method test_compile (line 227) | def test_compile(self):
    method tearDown (line 235) | def tearDown(self):
  class CompilerImplementation (line 240) | class CompilerImplementation(TestCase):
    method setUp (line 241) | def setUp(self):
    method tearDown (line 245) | def tearDown(self):
    method _test_compiler (line 248) | def _test_compiler(self, compiler_cls_str, infile, expected):
    method test_sass (line 260) | def test_sass(self):
    method test_coffeescript (line 267) | def test_coffeescript(self):
    method test_less (line 274) | def test_less(self):
    method test_es6 (line 281) | def test_es6(self):
    method test_typescript (line 288) | def test_typescript(self):
    method test_stylus (line 295) | def test_stylus(self):
    method test_livescript (line 302) | def test_livescript(self):

FILE: tests/tests/test_compressor.py
  class CompressorTest (line 32) | class CompressorTest(TestCase):
    method setUp (line 33) | def setUp(self):
    method test_js_compressor_class (line 38) | def test_js_compressor_class(self):
    method test_css_compressor_class (line 41) | def test_css_compressor_class(self):
    method test_concatenate_and_rewrite (line 44) | def test_concatenate_and_rewrite(self):
    method test_concatenate (line 52) | def test_concatenate(self):
    method test_encoded_content (line 60) | def test_encoded_content(self, mock):
    method test_encoded_content_output (line 68) | def test_encoded_content_output(self):
    method test_relative_path (line 78) | def test_relative_path(self):
    method test_base_path (line 85) | def test_base_path(self):
    method test_absolute_path (line 91) | def test_absolute_path(self):
    method test_template_name (line 101) | def test_template_name(self):
    method test_template_name_separator (line 113) | def test_template_name_separator(self):
    method test_compile_templates (line 124) | def test_compile_templates(self):
    method test_embeddable (line 145) | def test_embeddable(self):
    method test_construct_asset_path (line 159) | def test_construct_asset_path(self):
    method test_concatenate_with_url_rewrite (line 169) | def test_concatenate_with_url_rewrite(self) -> None:
    method test_concatenate_with_url_rewrite_data_uri (line 214) | def test_concatenate_with_url_rewrite_data_uri(self):
    method test_concatenate_css_with_sourcemap (line 235) | def test_concatenate_css_with_sourcemap(self) -> None:
    method test_concatenate_js_with_sourcemap (line 273) | def test_concatenate_js_with_sourcemap(self) -> None:
    method test_concatenate_without_rewrite_path_re (line 305) | def test_concatenate_without_rewrite_path_re(self) -> None:
    method test_concatenate_without_output_filename (line 344) | def test_concatenate_without_output_filename(self) -> None:
    method test_concatenate_without_file_sep (line 383) | def test_concatenate_without_file_sep(self) -> None:
    method test_legacy_concatenate_and_rewrite (line 417) | def test_legacy_concatenate_and_rewrite(self) -> None:
    method test_legacy_concatenate_and_rewrite_with_data_uri (line 466) | def test_legacy_concatenate_and_rewrite_with_data_uri(self) -> None:
    method test_compressor_subprocess_unicode (line 492) | def test_compressor_subprocess_unicode(self):
    method tearDown (line 505) | def tearDown(self):
  class CompressorImplementationTest (line 509) | class CompressorImplementationTest(TestCase):
    method setUp (line 512) | def setUp(self):
    method tearDown (line 516) | def tearDown(self):
    method _test_compressor (line 519) | def _test_compressor(self, compressor_cls, compress_type, expected_file):
    method test_jsmin (line 537) | def test_jsmin(self):
    method test_csshtmljsminify (line 544) | def test_csshtmljsminify(self):
    method test_uglifyjs (line 557) | def test_uglifyjs(self):
    method test_terser (line 565) | def test_terser(self):
    method test_yuglify (line 573) | def test_yuglify(self):
    method test_cssmin (line 586) | def test_cssmin(self):
    method test_closure (line 595) | def test_closure(self):
    method test_yui_js (line 604) | def test_yui_js(self):
    method test_yui_css (line 613) | def test_yui_css(self):
    method test_csstidy (line 621) | def test_csstidy(self):

FILE: tests/tests/test_conf.py
  class TestSettings (line 9) | class TestSettings(TestCase):
    method test_3unicode (line 10) | def test_3unicode(self):
    method test_2unicode (line 14) | def test_2unicode(self):
    method test_2bytes (line 18) | def test_2bytes(self):
    method test_expected_splitting (line 22) | def test_expected_splitting(self):
    method test_expected_preservation (line 27) | def test_expected_preservation(self):
    method test_win_path_preservation (line 32) | def test_win_path_preservation(self):
    method test_tuples_are_normal (line 36) | def test_tuples_are_normal(self):

FILE: tests/tests/test_forms.py
  class PipelineFormMediaTests (line 43) | class PipelineFormMediaTests(TestCase):
    method test_css_packages_with_pipeline_enabled (line 47) | def test_css_packages_with_pipeline_enabled(self):
    method test_css_packages_with_pipeline_disabled (line 90) | def test_css_packages_with_pipeline_disabled(self):
    method test_js_packages_with_pipeline_enabled (line 136) | def test_js_packages_with_pipeline_enabled(self):
    method test_js_packages_with_pipeline_disabled (line 174) | def test_js_packages_with_pipeline_disabled(self):

FILE: tests/tests/test_glob.py
  function local_path (line 11) | def local_path(path):
  class GlobTest (line 15) | class GlobTest(TestCase):
    method normpath (line 16) | def normpath(self, *parts):
    method mktemp (line 19) | def mktemp(self, *parts):
    method assertSequenceEqual (line 27) | def assertSequenceEqual(self, l1, l2):
    method setUp (line 30) | def setUp(self):
    method glob (line 41) | def glob(self, *parts):
    method tearDown (line 48) | def tearDown(self):
    method test_glob_literal (line 52) | def test_glob_literal(self):
    method test_glob_one_directory (line 57) | def test_glob_one_directory(self):
    method test_glob_nested_directory (line 68) | def test_glob_nested_directory(self):
    method test_glob_directory_names (line 84) | def test_glob_directory_names(self):
    method test_glob_directory_with_trailing_slash (line 95) | def test_glob_directory_with_trailing_slash(self):

FILE: tests/tests/test_middleware.py
  function dummy_get_response (line 10) | def dummy_get_response(request):
  class MiddlewareTest (line 14) | class MiddlewareTest(TestCase):
    method setUp (line 17) | def setUp(self):
    method test_middleware_html (line 28) | def test_middleware_html(self):
    method test_middleware_text (line 37) | def test_middleware_text(self):
    method test_middleware_not_used (line 47) | def test_middleware_not_used(self):

FILE: tests/tests/test_packager.py
  class PackagerTest (line 8) | class PackagerTest(TestCase):
    method setUp (line 9) | def setUp(self):
    method test_package_for (line 12) | def test_package_for(self):
    method test_templates (line 32) | def test_templates(self):
    method tearDown (line 47) | def tearDown(self):

FILE: tests/tests/test_storage.py
  class PipelineNoPathStorage (line 16) | class PipelineNoPathStorage(PipelineStorage):
    method path (line 19) | def path(self, *args):
    method delete (line 22) | def delete(self, *args):
    method exists (line 25) | def exists(self, *args):
    method save (line 28) | def save(self, *args):
    method open (line 31) | def open(self, *args):
    method listdir (line 34) | def listdir(self, *args):
  class DummyCSSCompiler (line 38) | class DummyCSSCompiler(DummyCompiler):
    method match_file (line 43) | def match_file(self, path):
  class StorageTest (line 47) | class StorageTest(TestCase):
    method tearDown (line 48) | def tearDown(self):
    method test_post_process_dry_run (line 52) | def test_post_process_dry_run(self):
    method test_post_process (line 62) | def test_post_process(self):
    method test_post_process_no_path (line 77) | def test_post_process_no_path(self):
    method test_nonexistent_file_pipeline_finder (line 88) | def test_nonexistent_file_pipeline_finder(self):
    method test_nonexistent_file_pipeline_finder_find_all (line 93) | def test_nonexistent_file_pipeline_finder_find_all(self):
    method test_nonexistent_file_pipeline_finder_all (line 102) | def test_nonexistent_file_pipeline_finder_all(self):
    method test_nonexistent_file_cached_finder (line 113) | def test_nonexistent_file_cached_finder(self):
    method test_nonexistent_double_extension_file_pipeline_finder (line 118) | def test_nonexistent_double_extension_file_pipeline_finder(self):
    method test_nonexistent_double_extension_file_cached_finder (line 125) | def test_nonexistent_double_extension_file_cached_finder(self):
    method test_manifest_finder_finds_stylesheet (line 130) | def test_manifest_finder_finds_stylesheet(self):
    method test_manifest_finder_finds_all_stylesheet (line 138) | def test_manifest_finder_finds_all_stylesheet(self):

FILE: tests/tests/test_template.py
  class JinjaTest (line 9) | class JinjaTest(TestCase):
    method setUp (line 10) | def setUp(self):
    method test_no_package (line 16) | def test_no_package(self):
    method test_package_css (line 22) | def test_package_css(self):
    method test_package_css_disabled (line 30) | def test_package_css_disabled(self):
    method test_package_js (line 39) | def test_package_js(self):
    method test_package_js_async (line 46) | def test_package_js_async(self):
    method test_package_js_defer (line 53) | def test_package_js_defer(self):
    method test_package_js_async_defer (line 60) | def test_package_js_async_defer(self):
  class DjangoTest (line 68) | class DjangoTest(TestCase):
    method render_template (line 69) | def render_template(self, template):
    method test_compressed_empty (line 72) | def test_compressed_empty(self):
    method test_compressed_css (line 78) | def test_compressed_css(self):
    method test_compressed_css_media (line 87) | def test_compressed_css_media(self):
    method test_compressed_css_title (line 96) | def test_compressed_css_title(self):
    method test_compressed_js (line 105) | def test_compressed_js(self):
    method test_compressed_js_async (line 114) | def test_compressed_js_async(self):
    method test_compressed_js_defer (line 123) | def test_compressed_js_defer(self):
    method test_compressed_js_async_defer (line 132) | def test_compressed_js_async_defer(self):

FILE: tests/tests/test_utils.py
  class UtilTest (line 8) | class UtilTest(TestCase):
    method test_guess_type (line 9) | def test_guess_type(self):
    method test_mimetypes_are_str (line 14) | def test_mimetypes_are_str(self):

FILE: tests/tests/test_views.py
  class ServeStaticViewsTest (line 14) | class ServeStaticViewsTest(TestCase):
    method setUp (line 15) | def setUp(self):
    method tearDown (line 24) | def tearDown(self):
    method test_found (line 30) | def test_found(self):
    method test_not_found (line 33) | def test_not_found(self):
    method test_debug_false (line 37) | def test_debug_false(self):
    method test_debug_false_and_insecure (line 44) | def test_debug_false_and_insecure(self):
    method test_pipeline_enabled_and_found (line 48) | def test_pipeline_enabled_and_found(self):
    method test_pipeline_enabled_and_not_found (line 53) | def test_pipeline_enabled_and_not_found(self):
    method test_collector_disabled_and_found (line 57) | def test_collector_disabled_and_found(self):
    method test_collector_disabled_and_not_found (line 62) | def test_collector_disabled_and_not_found(self):
    method _write_content (line 65) | def _write_content(self, content="abc123"):
    method _test_found (line 70) | def _test_found(self, **kwargs):
    method _test_not_found (line 84) | def _test_not_found(self, filename):

FILE: tests/utils.py
  function _ (line 7) | def _(path):
  class pipeline_settings (line 12) | class pipeline_settings(override_settings):
    method __init__ (line 13) | def __init__(self, **kwargs):
Condensed preview — 137 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (273K chars).
[
  {
    "path": ".flake8",
    "chars": 76,
    "preview": "[flake8]\nmax-line-length = 88\nexclude =\n    .tox\n    node_modules\n    *env/\n"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 119,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "chars": 987,
    "preview": "name: Release\n\non:\n  push:\n    tags:\n    - '*'\n\njobs:\n  build:\n    if: github.repository == 'jazzband/django-pipeline'\n "
  },
  {
    "path": ".github/workflows/test.yml",
    "chars": 2959,
    "preview": "name: Test\n\non: [push, pull_request]\n\njobs:\n  build:\n    name: build (Python ${{ matrix.python-version }}, Django ${{ ma"
  },
  {
    "path": ".gitignore",
    "chars": 354,
    "preview": ".AppleDouble\n*.pyc\n:2e_*\n*.tmproj\n.*.swp\n*.swo\nbuild\ndist\nMANIFEST\ndocs/_build/\n*.egg-info\n.coverage\ncoverage/\ncoverage."
  },
  {
    "path": ".pre-commit-config.yaml",
    "chars": 445,
    "preview": "repos:\n  - repo: https://github.com/psf/black\n    rev: 24.8.0\n    hooks:\n      - id: black\n\n  - repo: https://github.com"
  },
  {
    "path": ".readthedocs.yaml",
    "chars": 237,
    "preview": "# .readthedocs.yaml\n# Read the Docs configuration file\n# See https://docs.readthedocs.io/en/stable/config-file/v2.html f"
  },
  {
    "path": "AUTHORS",
    "chars": 5062,
    "preview": "Pipeline is a fork of django-compress which was originally created by\nAndreas Pelme <andreas@pelme.se> in 2008.\n\nThese p"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 2375,
    "preview": "# Code of Conduct\n\nAs contributors and maintainers of the Jazzband projects, and in the interest of\nfostering an open an"
  },
  {
    "path": "CONTRIBUTING.rst",
    "chars": 1680,
    "preview": ".. image:: https://jazzband.co/static/img/jazzband.svg\n   :target: https://jazzband.co/\n   :alt: Jazzband\n\nThis is a `Ja"
  },
  {
    "path": "HISTORY.rst",
    "chars": 7901,
    "preview": ".. :changelog:\n\nHistory\n=======\n\n4.1.0\n=====\n* Add support for Python 3.13\n* Add support for Django 5.2\n\n4.0.0\n=====\n* D"
  },
  {
    "path": "LICENSE",
    "chars": 1149,
    "preview": "Copyright (©) 2008 Andreas Pelme <andreas@pelme.se>\nCopyright (©) 2011-2018 Timothée Peignier <timothee.peignier@tryphon"
  },
  {
    "path": "MANIFEST.in",
    "chars": 456,
    "preview": "recursive-include pipeline/templates *.html *.jinja\nrecursive-include pipeline/jinja2 *.html *.jinja\ninclude AUTHORS LIC"
  },
  {
    "path": "README.rst",
    "chars": 3777,
    "preview": "Pipeline\n========\n\n.. image:: https://jazzband.co/static/img/badge.svg\n    :alt: Jazzband\n    :target: https://jazzband."
  },
  {
    "path": "docs/Makefile",
    "chars": 4626,
    "preview": "# Makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHINXBUILD "
  },
  {
    "path": "docs/compilers.rst",
    "chars": 6543,
    "preview": ".. _ref-compilers:\n\n=========\nCompilers\n=========\n\nTypeScript compiler\n======================\n\nThe TypeScript compiler u"
  },
  {
    "path": "docs/compressors.rst",
    "chars": 8077,
    "preview": ".. _ref-compressors:\n\n===========\nCompressors\n===========\n\n\nYuglify compressor\n==================\n\nThe Yuglify compresso"
  },
  {
    "path": "docs/conf.py",
    "chars": 7237,
    "preview": "# Pipeline documentation build configuration file, created by\n# sphinx-quickstart on Sat Apr 30 17:47:55 2011.\n#\n# This "
  },
  {
    "path": "docs/configuration.rst",
    "chars": 7583,
    "preview": ".. _ref-configuration:\n\n=============\nConfiguration\n=============\n\n\nConfiguration and list of available settings for Pip"
  },
  {
    "path": "docs/index.rst",
    "chars": 725,
    "preview": "Introduction\n============\n\nPipeline is an asset packaging library for Django, providing\nboth CSS and JavaScript concaten"
  },
  {
    "path": "docs/installation.rst",
    "chars": 2177,
    "preview": ".. _ref-installation:\n\n============\nInstallation\n============\n\n1. Either check out Pipeline from GitHub_ or to pull a re"
  },
  {
    "path": "docs/make.bat",
    "chars": 4529,
    "preview": "@ECHO OFF\n\nREM Command file for Sphinx documentation\n\nif \"%SPHINXBUILD%\" == \"\" (\n\tset SPHINXBUILD=sphinx-build\n)\nset BUI"
  },
  {
    "path": "docs/signals.rst",
    "chars": 688,
    "preview": ".. _ref-signals:\n\n=======\nSignals\n=======\n\nList of all signals sent by pipeline.\n\ncss_compressed\n--------------\n\n**pipel"
  },
  {
    "path": "docs/storages.rst",
    "chars": 4383,
    "preview": ".. _ref-storages:\n\n========\nStorages\n========\n\n\nUsing with staticfiles\n======================\n\nPipeline is providing a s"
  },
  {
    "path": "docs/templates.rst",
    "chars": 3220,
    "preview": ".. _ref-templates:\n\n====================\nJavascript Templates\n====================\n\nPipeline allows you to use javascrip"
  },
  {
    "path": "docs/usage.rst",
    "chars": 4772,
    "preview": ".. _ref-usage:\n\n=====\nUsage\n=====\n\nDescribes how to use Pipeline when it is installed and configured.\n\nTemplatetags\n===="
  },
  {
    "path": "docs/using.rst",
    "chars": 1424,
    "preview": ".. _ref-using:\n\n====================\nSites using Pipeline\n====================\n\nThe following sites are a partial list o"
  },
  {
    "path": "package.json",
    "chars": 786,
    "preview": "{\n  \"name\": \"django-pipeline-tests\",\n  \"private\": true,\n  \"version\": \"1.0.0\",\n  \"description\": \"Pipeline is an asset pac"
  },
  {
    "path": "pipeline/__init__.py",
    "chars": 754,
    "preview": "PackageNotFoundError = None\nDistributionNotFound = None\ntry:\n    from importlib.metadata import PackageNotFoundError\n   "
  },
  {
    "path": "pipeline/collector.py",
    "chars": 3535,
    "preview": "import os\n\nimport django\nfrom django.contrib.staticfiles import finders\nfrom django.contrib.staticfiles.storage import s"
  },
  {
    "path": "pipeline/compilers/__init__.py",
    "chars": 6340,
    "preview": "import os\nimport shutil\nimport subprocess\nfrom tempfile import NamedTemporaryFile\n\nfrom django.contrib.staticfiles impor"
  },
  {
    "path": "pipeline/compilers/coffee.py",
    "chars": 666,
    "preview": "from pipeline.compilers import SubProcessCompiler\nfrom pipeline.conf import settings\n\n\nclass CoffeeScriptCompiler(SubPro"
  },
  {
    "path": "pipeline/compilers/es6.py",
    "chars": 602,
    "preview": "from pipeline.compilers import SubProcessCompiler\nfrom pipeline.conf import settings\n\n\nclass ES6Compiler(SubProcessCompi"
  },
  {
    "path": "pipeline/compilers/less.py",
    "chars": 650,
    "preview": "from os.path import dirname\n\nfrom pipeline.compilers import SubProcessCompiler\nfrom pipeline.conf import settings\n\n\nclas"
  },
  {
    "path": "pipeline/compilers/livescript.py",
    "chars": 625,
    "preview": "from pipeline.compilers import SubProcessCompiler\nfrom pipeline.conf import settings\n\n\nclass LiveScriptCompiler(SubProce"
  },
  {
    "path": "pipeline/compilers/sass.py",
    "chars": 499,
    "preview": "from os.path import dirname\n\nfrom pipeline.compilers import SubProcessCompiler\nfrom pipeline.conf import settings\n\n\nclas"
  },
  {
    "path": "pipeline/compilers/stylus.py",
    "chars": 485,
    "preview": "from os.path import dirname\n\nfrom pipeline.compilers import SubProcessCompiler\nfrom pipeline.conf import settings\n\n\nclas"
  },
  {
    "path": "pipeline/compilers/typescript.py",
    "chars": 589,
    "preview": "from pipeline.compilers import SubProcessCompiler\nfrom pipeline.conf import settings\n\n\nclass TypeScriptCompiler(SubProce"
  },
  {
    "path": "pipeline/compressors/__init__.py",
    "chars": 15311,
    "preview": "from __future__ import annotations\n\nimport base64\nimport os\nimport posixpath\nimport re\nimport subprocess\nimport warnings"
  },
  {
    "path": "pipeline/compressors/closure.py",
    "chars": 290,
    "preview": "from pipeline.compressors import SubProcessCompressor\nfrom pipeline.conf import settings\n\n\nclass ClosureCompressor(SubPr"
  },
  {
    "path": "pipeline/compressors/csshtmljsminify.py",
    "chars": 507,
    "preview": "from pipeline.compressors import CompressorBase\n\n\nclass CssHtmlJsMinifyCompressor(CompressorBase):\n    \"\"\"\n    CSS, HTML"
  },
  {
    "path": "pipeline/compressors/cssmin.py",
    "chars": 290,
    "preview": "from pipeline.compressors import SubProcessCompressor\nfrom pipeline.conf import settings\n\n\nclass CSSMinCompressor(SubPro"
  },
  {
    "path": "pipeline/compressors/csstidy.py",
    "chars": 586,
    "preview": "from django.core.files import temp as tempfile\n\nfrom pipeline.compressors import SubProcessCompressor\nfrom pipeline.conf"
  },
  {
    "path": "pipeline/compressors/jsmin.py",
    "chars": 306,
    "preview": "from pipeline.compressors import CompressorBase\n\n\nclass JSMinCompressor(CompressorBase):\n    \"\"\"\n    JS compressor based"
  },
  {
    "path": "pipeline/compressors/terser.py",
    "chars": 348,
    "preview": "from pipeline.compressors import SubProcessCompressor\nfrom pipeline.conf import settings\n\n\nclass TerserCompressor(SubPro"
  },
  {
    "path": "pipeline/compressors/uglifyjs.py",
    "chars": 354,
    "preview": "from pipeline.compressors import SubProcessCompressor\nfrom pipeline.conf import settings\n\n\nclass UglifyJSCompressor(SubP"
  },
  {
    "path": "pipeline/compressors/yuglify.py",
    "chars": 563,
    "preview": "from pipeline.compressors import SubProcessCompressor\nfrom pipeline.conf import settings\n\n\nclass YuglifyCompressor(SubPr"
  },
  {
    "path": "pipeline/compressors/yui.py",
    "chars": 547,
    "preview": "from pipeline.compressors import SubProcessCompressor\nfrom pipeline.conf import settings\n\n\nclass YUICompressor(SubProces"
  },
  {
    "path": "pipeline/conf.py",
    "chars": 3380,
    "preview": "import os\nimport shlex\nfrom collections.abc import MutableMapping\n\nfrom django.conf import settings as _settings\nfrom dj"
  },
  {
    "path": "pipeline/exceptions.py",
    "chars": 366,
    "preview": "class PipelineException(Exception):\n    pass\n\n\nclass PackageNotFound(PipelineException):\n    pass\n\n\nclass CompilerError("
  },
  {
    "path": "pipeline/finders.py",
    "chars": 3333,
    "preview": "from itertools import chain\nfrom os.path import normpath\n\nfrom django.contrib.staticfiles.finders import (\n    AppDirect"
  },
  {
    "path": "pipeline/forms.py",
    "chars": 9287,
    "preview": "\"\"\"Support for referencing Pipeline packages in forms and widgets.\"\"\"\n\nfrom django.contrib.staticfiles.storage import st"
  },
  {
    "path": "pipeline/glob.py",
    "chars": 1930,
    "preview": "import fnmatch\nimport os\nimport re\n\nfrom django.contrib.staticfiles.storage import staticfiles_storage\n\n__all__ = [\"glob"
  },
  {
    "path": "pipeline/jinja2/__init__.py",
    "chars": 3109,
    "preview": "from django.contrib.staticfiles.storage import staticfiles_storage\nfrom jinja2 import TemplateSyntaxError, nodes\nfrom ji"
  },
  {
    "path": "pipeline/jinja2/pipeline/css.jinja",
    "chars": 157,
    "preview": "<link href=\"{{ url }}\" rel=\"stylesheet\" type=\"{{ type }}\"{% if media %} media=\"{{ media }}\"{% endif %}{% if charset %} c"
  },
  {
    "path": "pipeline/jinja2/pipeline/inline_js.jinja",
    "chars": 124,
    "preview": "<script{% if async %} async{% endif %}{% if defer %} defer{% endif %} type=\"text/javascript\">\n  {{ source|safe }}\n</scri"
  },
  {
    "path": "pipeline/jinja2/pipeline/js.jinja",
    "chars": 130,
    "preview": "<script{% if async %} async{% endif %}{% if defer %} defer{% endif %} type=\"{{ type }}\" src=\"{{ url }}\" charset=\"utf-8\">"
  },
  {
    "path": "pipeline/middleware.py",
    "chars": 928,
    "preview": "from django.core.exceptions import MiddlewareNotUsed\nfrom django.utils.deprecation import MiddlewareMixin\nfrom django.ut"
  },
  {
    "path": "pipeline/packager.py",
    "chars": 5753,
    "preview": "from django.contrib.staticfiles.finders import find, get_finders\nfrom django.contrib.staticfiles.storage import staticfi"
  },
  {
    "path": "pipeline/signals.py",
    "chars": 87,
    "preview": "from django.dispatch import Signal\n\ncss_compressed = Signal()\njs_compressed = Signal()\n"
  },
  {
    "path": "pipeline/storage.py",
    "chars": 3792,
    "preview": "import gzip\nfrom io import BytesIO\n\nfrom django import get_version as django_version\nfrom django.contrib.staticfiles.sto"
  },
  {
    "path": "pipeline/templates/pipeline/compile_error.html",
    "chars": 1044,
    "preview": "<div id=\"django-pipeline-error-{{package_name}}\" class=\"django-pipeline-error\"\n     style=\"display: none; border: 2px #D"
  },
  {
    "path": "pipeline/templates/pipeline/css.html",
    "chars": 190,
    "preview": "<link href=\"{{ url }}\" rel=\"stylesheet\" type=\"{{ type }}\" media=\"{{ media|default:\"all\" }}\"{% if title %} title=\"{{ titl"
  },
  {
    "path": "pipeline/templates/pipeline/css.jinja",
    "chars": 157,
    "preview": "<link href=\"{{ url }}\" rel=\"stylesheet\" type=\"{{ type }}\"{% if media %} media=\"{{ media }}\"{% endif %}{% if charset %} c"
  },
  {
    "path": "pipeline/templates/pipeline/inline_js.html",
    "chars": 123,
    "preview": "<script{% if async %} async{% endif %}{% if defer %} defer{% endif %} type=\"text/javascript\">\n  {{ source|safe }}\n</scri"
  },
  {
    "path": "pipeline/templates/pipeline/inline_js.jinja",
    "chars": 124,
    "preview": "<script{% if async %} async{% endif %}{% if defer %} defer{% endif %} type=\"text/javascript\">\n  {{ source|safe }}\n</scri"
  },
  {
    "path": "pipeline/templates/pipeline/js.html",
    "chars": 129,
    "preview": "<script{% if async %} async{% endif %}{% if defer %} defer{% endif %} type=\"{{ type }}\" src=\"{{ url }}\" charset=\"utf-8\">"
  },
  {
    "path": "pipeline/templates/pipeline/js.jinja",
    "chars": 130,
    "preview": "<script{% if async %} async{% endif %}{% if defer %} defer{% endif %} type=\"{{ type }}\" src=\"{{ url }}\" charset=\"utf-8\">"
  },
  {
    "path": "pipeline/templatetags/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "pipeline/templatetags/pipeline.py",
    "chars": 8025,
    "preview": "import logging\nimport re\nimport subprocess\n\nfrom django import template\nfrom django.contrib.staticfiles.storage import s"
  },
  {
    "path": "pipeline/utils.py",
    "chars": 2040,
    "preview": "try:\n    import fcntl\nexcept ImportError:\n    # windows\n    fcntl = None\n\nimport importlib\nimport mimetypes\nimport os\nim"
  },
  {
    "path": "pipeline/views.py",
    "chars": 1609,
    "preview": "from django.conf import settings as django_settings\nfrom django.core.exceptions import ImproperlyConfigured\nfrom django."
  },
  {
    "path": "pyproject.toml",
    "chars": 2312,
    "preview": "[build-system]\nrequires = [\"setuptools>=64\", \"setuptools_scm[toml]>=8\"]\nbuild-backend = \"setuptools.build_meta\"\n\n[projec"
  },
  {
    "path": "tests/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/assets/compilers/coffee/expected.js",
    "chars": 154,
    "preview": "(function() {\n  var cube, square;\n\n  square = function(x) {\n    return x * x;\n  };\n\n  cube = function(x) {\n    return sq"
  },
  {
    "path": "tests/assets/compilers/coffee/input.coffee",
    "chars": 52,
    "preview": "square = (x) -> x * x\ncube   = (x) -> square(x) * x\n"
  },
  {
    "path": "tests/assets/compilers/es6/expected.js",
    "chars": 475,
    "preview": "\"use strict\";\n\n// Expression bodies\nvar odds = evens.map(function (v) {\n  return v + 1;\n});\nvar nums = evens.map(functio"
  },
  {
    "path": "tests/assets/compilers/es6/input.es6",
    "chars": 343,
    "preview": "// Expression bodies\nvar odds = evens.map(v => v + 1);\nvar nums = evens.map((v, i) => v + i);\n\n// Statement bodies\nnums."
  },
  {
    "path": "tests/assets/compilers/less/expected.css",
    "chars": 21,
    "preview": ".a {\n  width: 1px;\n}\n"
  },
  {
    "path": "tests/assets/compilers/less/input.less",
    "chars": 38,
    "preview": "@a: 1;\n\n.a {\n    width: (@a + 0px);\n}\n"
  },
  {
    "path": "tests/assets/compilers/livescript/expected.js",
    "chars": 90,
    "preview": "(function(){\n  var times;\n  times = function(x, y){\n    return x * y;\n  };\n}).call(this);\n"
  },
  {
    "path": "tests/assets/compilers/livescript/input.ls",
    "chars": 26,
    "preview": "times = (x, y) ->\n  x * y\n"
  },
  {
    "path": "tests/assets/compilers/scss/expected.css",
    "chars": 95,
    "preview": ".a .b {\n  display: none;\n}\n\n.c .d {\n  display: block;\n}\n\n/*# sourceMappingURL=input.css.map */\n"
  },
  {
    "path": "tests/assets/compilers/scss/input.scss",
    "chars": 91,
    "preview": ".a {\n    .b {\n        display: none;\n    }\n}\n.c {\n    .d {\n        display: block;\n    }\n}\n"
  },
  {
    "path": "tests/assets/compilers/stylus/expected.css",
    "chars": 22,
    "preview": ".a {\n  color: #000;\n}\n"
  },
  {
    "path": "tests/assets/compilers/stylus/input.styl",
    "chars": 19,
    "preview": ".a\n    color: black"
  },
  {
    "path": "tests/assets/compilers/typescript/expected.js",
    "chars": 154,
    "preview": "function getName(u) {\n    return \"\".concat(u.firstName, \" \").concat(u.lastName);\n}\nvar userName = getName({ firstName: \""
  },
  {
    "path": "tests/assets/compilers/typescript/input.ts",
    "chars": 254,
    "preview": "type FullName = string;\n\ninterface User {\n    firstName: string;\n    lastName: string;\n}\n\n\nfunction getName(u: User): Fu"
  },
  {
    "path": "tests/assets/compressors/closure.js",
    "chars": 157,
    "preview": "(function(){(function(){window.concat=function(){console.log(arguments)}})();(function(){window.cat=function(){console.l"
  },
  {
    "path": "tests/assets/compressors/csshtmljsminify.css",
    "chars": 65,
    "preview": "@charset \"utf-8\";.concat{display:none}.concatenate{display:block}"
  },
  {
    "path": "tests/assets/compressors/csshtmljsminify.js",
    "chars": 159,
    "preview": "(function(){(function(){window.concat=function(){console.log(arguments);}}());(function(){window.cat=function(){console."
  },
  {
    "path": "tests/assets/compressors/cssmin.css",
    "chars": 48,
    "preview": ".concat{display:none}.concatenate{display:block}"
  },
  {
    "path": "tests/assets/compressors/csstidy.css",
    "chars": 50,
    "preview": ".concat{display:none;}.concatenate{display:block;}"
  },
  {
    "path": "tests/assets/compressors/jsmin.js",
    "chars": 159,
    "preview": "(function(){(function(){window.concat=function(){console.log(arguments);}}());(function(){window.cat=function(){console."
  },
  {
    "path": "tests/assets/compressors/slimit.js",
    "chars": 161,
    "preview": "(function(){(function(){window.concat=function(){console.log(arguments);};}());(function(){window.cat=function(){console"
  },
  {
    "path": "tests/assets/compressors/terser.js",
    "chars": 125,
    "preview": "(function(){window.concat=function(){console.log(arguments)},window.cat=function(){console.log(\"hello world\")}}).call(th"
  },
  {
    "path": "tests/assets/compressors/uglifyjs.js",
    "chars": 157,
    "preview": "(function(){(function(){window.concat=function(){console.log(arguments)}})();(function(){window.cat=function(){console.l"
  },
  {
    "path": "tests/assets/compressors/yuglify.css",
    "chars": 49,
    "preview": ".concat{display:none}.concatenate{display:block}\n"
  },
  {
    "path": "tests/assets/compressors/yuglify.js",
    "chars": 154,
    "preview": "(function(){!function(){window.concat=function(){console.log(arguments)}}(),function(){window.cat=function(){console.log"
  },
  {
    "path": "tests/assets/compressors/yui.css",
    "chars": 48,
    "preview": ".concat{display:none}.concatenate{display:block}"
  },
  {
    "path": "tests/assets/compressors/yui.js",
    "chars": 156,
    "preview": "(function(){(function(){window.concat=function(){console.log(arguments)}}());(function(){window.cat=function(){console.l"
  },
  {
    "path": "tests/assets/css/first.css",
    "chars": 29,
    "preview": ".concat {\n  display: none;\n}\n"
  },
  {
    "path": "tests/assets/css/nested/nested.css",
    "chars": 2461,
    "preview": ".data-url {\n  background-image: url(data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22"
  },
  {
    "path": "tests/assets/css/second.css",
    "chars": 35,
    "preview": ".concatenate {\n  display: block;\n}\n"
  },
  {
    "path": "tests/assets/css/sourcemap.css",
    "chars": 378,
    "preview": "div {\n  display: inline;\n}\n\nspan {\n  display: block;\n}\n\n\n//#  sourceMappingURL=sourcemap1.css.map\n\n//@ sourceMappingURL="
  },
  {
    "path": "tests/assets/css/unicode.css",
    "chars": 54,
    "preview": ".some_class {\n  // Some unicode\n  content: \"áéíóú\";\n}\n"
  },
  {
    "path": "tests/assets/css/urls.css",
    "chars": 1337,
    "preview": ".embedded-url-svg {\n  background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 32 32' xmlns='http://ww"
  },
  {
    "path": "tests/assets/js/application.js",
    "chars": 47,
    "preview": "function test() {\n  alert('this is a test');\n}\n"
  },
  {
    "path": "tests/assets/js/dummy.coffee",
    "chars": 52,
    "preview": "square = (x) -> x * x\ncube   = (x) -> square(x) * x\n"
  },
  {
    "path": "tests/assets/js/first.js",
    "chars": 98,
    "preview": "(function() {\n  window.concat = function() {\n    console.log(arguments);\n  }\n}()) // No semicolon\n"
  },
  {
    "path": "tests/assets/js/second.js",
    "chars": 84,
    "preview": "(function() {\n  window.cat = function() {\n    console.log(\"hello world\");\n  }\n}());\n"
  },
  {
    "path": "tests/assets/js/sourcemap.js",
    "chars": 332,
    "preview": "const abc = 123;\n\n\n//#  sourceMappingURL=sourcemap1.js.map\n\n//@ sourceMappingURL=sourcemap2.js.map  \n\n/*#  sourceMapping"
  },
  {
    "path": "tests/assets/templates/photo/detail.jst",
    "chars": 117,
    "preview": "<div class=\"photo\">\n <img src=\"<%= src %>\" />\n <div class=\"caption\">\n  <%= caption %> by <%= author %>\n </div>\n</div>"
  },
  {
    "path": "tests/assets/templates/photo/list.jst",
    "chars": 100,
    "preview": "<div class=\"photo\">\n <img src=\"<%= src %>\" />\n <div class=\"caption\">\n  <%= caption %>\n </div>\n</div>"
  },
  {
    "path": "tests/assets/templates/video/detail.jst",
    "chars": 106,
    "preview": "<div class=\"video\">\n <video src=\"<%= src %>\" />\n <div class=\"caption\">\n  <%= description %>\n </div>\n</div>"
  },
  {
    "path": "tests/models.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/settings.py",
    "chars": 6890,
    "preview": "import glob\nimport os\nimport shutil\n\n\ndef local_path(path):\n    return os.path.join(os.path.dirname(__file__), path)\n\n\nD"
  },
  {
    "path": "tests/templates/empty.html",
    "chars": 1,
    "preview": " "
  },
  {
    "path": "tests/templates/index.html",
    "chars": 205,
    "preview": "{% load pipeline %}\n<html>\n <head>\n  <meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\">\n  <title>Pipeli"
  },
  {
    "path": "tests/tests/__init__.py",
    "chars": 478,
    "preview": "import os\nimport sys\n\nif sys.platform.startswith(\"win\"):\n    os.environ.setdefault(\"NUMBER_OF_PROCESSORS\", \"1\")\n\n\nfrom ."
  },
  {
    "path": "tests/tests/models.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/tests/test_collector.py",
    "chars": 2281,
    "preview": "import os\n\nfrom django.contrib.staticfiles import finders\nfrom django.core.files.storage import FileSystemStorage\nfrom d"
  },
  {
    "path": "tests/tests/test_compiler.py",
    "chars": 9802,
    "preview": "import sys\nfrom unittest import skipIf, skipUnless\n\nfrom django.conf import settings\nfrom django.contrib.staticfiles.sto"
  },
  {
    "path": "tests/tests/test_compressor.py",
    "chars": 27193,
    "preview": "import base64\nimport os\nimport sys\n\ntry:\n    from unittest.mock import patch\nexcept ImportError:\n    from unittest.mock "
  },
  {
    "path": "tests/tests/test_conf.py",
    "chars": 1520,
    "preview": "import sys\nfrom unittest import skipIf, skipUnless\n\nfrom django.test import TestCase\n\nfrom pipeline.conf import Pipeline"
  },
  {
    "path": "tests/tests/test_forms.py",
    "chars": 6785,
    "preview": "from django import get_version as django_version\nfrom django.forms import Media\nfrom django.test import TestCase\n\nfrom p"
  },
  {
    "path": "tests/tests/test_glob.py",
    "chars": 3621,
    "preview": "import os\nimport shutil\n\nfrom django.core.files.base import ContentFile\nfrom django.core.files.storage import FileSystem"
  },
  {
    "path": "tests/tests/test_middleware.py",
    "chars": 1538,
    "preview": "from unittest.mock import patch\n\nfrom django.core.exceptions import MiddlewareNotUsed\nfrom django.http import HttpReques"
  },
  {
    "path": "tests/tests/test_packager.py",
    "chars": 1382,
    "preview": "from django.test import TestCase\n\nfrom pipeline.collector import default_collector\nfrom pipeline.packager import Package"
  },
  {
    "path": "tests/tests/test_storage.py",
    "chars": 4914,
    "preview": "from io import StringIO\n\nimport django\nfrom django.contrib.staticfiles import finders\nfrom django.contrib.staticfiles.st"
  },
  {
    "path": "tests/tests/test_template.py",
    "chars": 5329,
    "preview": "from django.template import Context, Template\nfrom django.test import TestCase\nfrom jinja2 import Environment, PackageLo"
  },
  {
    "path": "tests/tests/test_utils.py",
    "chars": 549,
    "preview": "import mimetypes\n\nfrom django.test import TestCase\n\nfrom pipeline.utils import guess_type\n\n\nclass UtilTest(TestCase):\n  "
  },
  {
    "path": "tests/tests/test_views.py",
    "chars": 3057,
    "preview": "from django.contrib.staticfiles.storage import staticfiles_storage\nfrom django.core.exceptions import ImproperlyConfigur"
  },
  {
    "path": "tests/urls.py",
    "chars": 326,
    "preview": "from django.contrib import admin\nfrom django.urls import path\nfrom django.views.generic import TemplateView\n\nurlpatterns"
  },
  {
    "path": "tests/utils.py",
    "chars": 599,
    "preview": "import os\n\nimport django\nfrom django.test import override_settings\n\n\ndef _(path):\n    # Make sure the path contains only"
  },
  {
    "path": "tests/views.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tox.ini",
    "chars": 1418,
    "preview": "[tox]\nenvlist =\n  pypy3-dj{41,42}\n  py{39,310,311}-dj41\n  py{39,310,311,312}-dj42\n  py{310,311,312}-dj50\n  py{310,311,31"
  }
]

About this extraction

This page contains the full source code of the cyberdelia/django-pipeline GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 137 files (247.0 KB), approximately 66.0k tokens, and a symbol index with 428 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!