Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add loader for create react app #33

Draft
wants to merge 4 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,8 @@
'.rst': 'restructuredtext',
'.txt': 'markdown',
'.md': 'markdown',
}
}

html_theme_options = {
"collapse_navigation" : False
}
126 changes: 126 additions & 0 deletions docs/docs/advanced_usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# Custom Loaders

## Writing a custom loader

Custom loaders allow you to implement your own means of extracting data from your manifest file. If your manifest
file is not the default structure of [webpack manifest plugin](https://github.com/shellscape/webpack-manifest-plugin), this is how you can tell `django-manifest-loader` how to read it.

First import the loader parent abstract class, and subclass it in your new loader class.

```python
from manifest_loader.loaders import LoaderABC

class MyCustomLoader(LoaderABC):
```

Your new loader must have two static methods that each take two required arguments:
`get_single_match(manifest, key)` and `get_multi_match(manifest, pattern)`.

```python
from manifest_loader.loaders import LoaderABC

class MyCustomLoader(LoaderABC):
@staticmethod
def get_single_match(manifest, key):
pass

@staticmethod
def get_multi_match(manifest, pattern):
pass
```

* `get_single_match` - returns a `String`, finds a single file in your manifest file, according to the `key`
* `get_multi_match` - returns a `List` of files in your manifest, according to the `pattern`
* `manifest` - this is your full manifest file, after being processed by `json.load()`. It will be a dictionary or list
depending on which it is in your manifest file.
* `key` - `String`; the argument passed into the `manifest` template tag. e.g.: in the template tag `{% manifest 'index.js' %}`,
the string `'index.js'` is sent to `get_single_match` as `key` (without surrounding quotes)
* `pattern` - `String`; the first argument passed into the `manifest_match` template tag. e.g.: in the template tag
`{% manifest_match '*.js' '<script src="{match}"></script>' %}`, the string `'*.js'` is sent to `get_multi_match`
as `pattern` (without surrounding quotes)

**Below is the code for the default loader, which is a good starting point:**

```python
import fnmatch
from manifest_loader.loaders import LoaderABC

class DefaultLoader(LoaderABC):
@staticmethod
def get_single_match(manifest, key):
return manifest.get(key, key)

@staticmethod
def get_multi_match(manifest, pattern):
matched_files = [file for file in manifest.keys() if
fnmatch.fnmatch(file, pattern)]
return [manifest.get(file) for file in matched_files]
```

In the above example, `get_single_match` retrieves the value on the `manifest` dictionary that matches the key `key`. If
the key does not exist on the dictionary, it instead returns the key.

`get_multi_match` uses the recommended `fnmatch` python standard library to do pattern matching. You could also use
regex in it's place. Here, it iterates through all the keys in the manifest file, and builds a list of the keys that
match the given `pattern`. It then returns a list of the values associated with those matched keys.

## Activating the custom loader

To put the custom loader into use it needs to be registered in your `settings.py`.

```python
# settings.py
from my_app.utils import MyCustomLoader

MANIFEST_LOADER = {
...
'loader': MyCustomLoader
}
```

## Contribute your custom loader

If you write a custom loader that you think would be helpful for others, or if you have a specific loader request, please either make a pull request or file an issue in this [project's Github](https://github.com/shonin/django-manifest-loader).

# URLs in Manifest File

If your manifest file points to full URLs, instead of file names, the full URL will be output instead of pointing to the static file directory in Django.

Example:

```json
{
"main.js": "http://localhost:8080/main.js"
}
```

```html
{% load manifest %}

<script src="{% manifest 'main.js' %}"></script>
```

Will output as:

```html
<script src="http://localhost:8080/main.js"></script>
```


# Tests and Code Coverage

Run unit tests and verify 100% code coverage with:

```
git clone https://github.com/shonin/django-manifest-loader.git
cd django-manifest-loader
pip install -e .

# run tests
python runtests.py

# check code coverage
pip install coverage
coverage run --source=manifest_loader/ runtests.py
coverage report
```
23 changes: 23 additions & 0 deletions docs/docs/loaders.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Loaders

## Default webpack loader

`manifest_loader.loaders.DefaultLoader`

The default loader expects the out-of-the-box manifest file as generated by webpack manifest plugin:


```json
{
"main.js": "main.cd3ab1.js"
}
```

It performs all operations against the keys, and always returns the values.

## Create React App loader

`manifest_loader.loaders.CreateReactAppLoader`

_The `create-recat-app` loader is in beta. If you use this loader please feel free to [open an issue](https://github.com/shonin/django-manifest-loader/issues/new) in Github letting me know if it works how you'd expect._

123 changes: 0 additions & 123 deletions docs/docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,126 +42,3 @@ turns into

This tag takes two arguments, a pattern to match against, according to the python `fnmatch` package rules,
and a string to input the file URLs into. The second argument must contain the string `{match}`, as it is replaced with the URLs.

# Advanced Usage

## Custom Loaders

Custom loaders allow you to implement your own means of extracting data from your manifest file. If your manifest
file is not the default structure of [webpack manifest plugin](https://github.com/shellscape/webpack-manifest-plugin), this is how you can tell `django-manifest-loader` how to read it.

First import the loader parent abstract class, and subclass it in your new loader class.

```python
from manifest_loader.loaders import LoaderABC

class MyCustomLoader(LoaderABC):
```

Your new loader must have two static methods that each take two required arguments:
`get_single_match(manifest, key)` and `get_multi_match(manifest, pattern)`.

```python
from manifest_loader.loaders import LoaderABC

class MyCustomLoader(LoaderABC):
@staticmethod
def get_single_match(manifest, key):
pass

@staticmethod
def get_multi_match(manifest, pattern):
pass
```

* `get_single_match` - returns a `String`, finds a single file in your manifest file, according to the `key`
* `get_multi_match` - returns a `List` of files in your manifest, according to the `pattern`
* `manifest` - this is your full manifest file, after being processed by `json.load()`. It will be a dictionary or list
depending on which it is in your manifest file.
* `key` - `String`; the argument passed into the `manifest` template tag. e.g.: in the template tag `{% manifest 'index.js' %}`,
the string `'index.js'` is sent to `get_single_match` as `key` (without surrounding quotes)
* `pattern` - `String`; the first argument passed into the `manifest_match` template tag. e.g.: in the template tag
`{% manifest_match '*.js' '<script src="{match}"></script>' %}`, the string `'*.js'` is sent to `get_multi_match`
as `pattern` (without surrounding quotes)

**Below is the code for the default loader, which is a good starting point:**

```python
import fnmatch
from manifest_loader.loaders import LoaderABC

class DefaultLoader(LoaderABC):
@staticmethod
def get_single_match(manifest, key):
return manifest.get(key, key)

@staticmethod
def get_multi_match(manifest, pattern):
matched_files = [file for file in manifest.keys() if
fnmatch.fnmatch(file, pattern)]
return [manifest.get(file) for file in matched_files]
```

In the above example, `get_single_match` retrieves the value on the `manifest` dictionary that matches the key `key`. If
the key does not exist on the dictionary, it instead returns the key.

`get_multi_match` uses the recommended `fnmatch` python standard library to do pattern matching. You could also use
regex in it's place. Here, it iterates through all the keys in the manifest file, and builds a list of the keys that
match the given `pattern`. It then returns a list of the values associated with those matched keys.

### Activating the custom loader

To put the custom loader into use it needs to be registered in your `settings.py`.

```python
# settings.py
from my_app.utils import MyCustomLoader

MANIFEST_LOADER = {
...
'loader': MyCustomLoader
}
```

## URLs in Manifest File

If your manifest file points to full URLs, instead of file names, the full URL will be output instead of pointing to the static file directory in Django.

Example:

```json
{
"main.js": "http://localhost:8080/main.js"
}
```

```html
{% load manifest %}

<script src="{% manifest 'main.js' %}"></script>
```

Will output as:

```html
<script src="http://localhost:8080/main.js"></script>
```


# Tests and Code Coverage

Run unit tests and verify 100% code coverage with:

```
git clone https://github.com/shonin/django-manifest-loader.git
cd django-manifest-loader
pip install -e .

# run tests
python runtests.py

# check code coverage
pip install coverage
coverage run --source=manifest_loader/ runtests.py
coverage report
```
4 changes: 3 additions & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ Django's built-in ``staticfiles`` app. Minimal configuraton, cache-busting, spli
Designed for webpack, ready for anything.

.. toctree::
:maxdepth: 1
:maxdepth: 2

docs/about_install
docs/usage
docs/loaders
docs/advanced_usage
docs/philosophy
docs/reference
docs/docs_license
Expand Down
27 changes: 26 additions & 1 deletion manifest_loader/loaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,29 @@ def get_single_match(manifest, key):
def get_multi_match(manifest, pattern):
matched_files = [file for file in manifest.keys() if
fnmatch.fnmatch(file, pattern)]
return [manifest.get(file) for file in matched_files]
return [manifest.get(file) for file in matched_files]


class CreateReactAppLoader(LoaderABC):
@staticmethod
def get_single_match(manifest, key):
files = manifest.get('files', {})
return files.get(key, key)

@staticmethod
def get_multi_match(manifest, pattern):
split_pattern = pattern.split(' ')
parent = split_pattern[0] if len(split_pattern) == 2 else 'entrypoints'
pattern = split_pattern[1] if len(split_pattern) == 2 else split_pattern[0]
files = manifest.get(parent, {})

if isinstance(files, dict):
matched_files = [file for file in files.keys() if
fnmatch.fnmatch(file, pattern)]
return [files.get(file) for file in matched_files]

elif isinstance(files, list):
return [file for file in files if fnmatch.fnmatch(file, pattern)]

return []