From 20e55871b139b0f6fb4968f080d877c086fbc754 Mon Sep 17 00:00:00 2001 From: Elliot Charney Date: Tue, 29 Dec 2020 21:20:00 -0800 Subject: [PATCH 1/4] add loader for create react app --- manifest_loader/loaders.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/manifest_loader/loaders.py b/manifest_loader/loaders.py index 4fabf57..49b3c96 100644 --- a/manifest_loader/loaders.py +++ b/manifest_loader/loaders.py @@ -23,4 +23,17 @@ 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] \ No newline at end of file + 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): + entry_points_list = manifest.get('entrypoints') + return [file for file in entry_points_list if + fnmatch.fnmatch(file, pattern)] From 501172c83b58c7df8b68f0752deafcc6a062e321 Mon Sep 17 00:00:00 2001 From: Elliot Charney Date: Wed, 30 Dec 2020 15:44:58 -0800 Subject: [PATCH 2/4] allow cra loader to pattern match against files and entrypoints --- manifest_loader/loaders.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/manifest_loader/loaders.py b/manifest_loader/loaders.py index 49b3c96..34930b2 100644 --- a/manifest_loader/loaders.py +++ b/manifest_loader/loaders.py @@ -34,6 +34,19 @@ def get_single_match(manifest, key): @staticmethod def get_multi_match(manifest, pattern): - entry_points_list = manifest.get('entrypoints') - return [file for file in entry_points_list if + 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 + 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 [] + From 315e71541f2fc43d4174954b5b4419ed02d28e61 Mon Sep 17 00:00:00 2001 From: Elliot Charney Date: Wed, 30 Dec 2020 19:23:23 -0800 Subject: [PATCH 3/4] fix split in cra loader --- manifest_loader/loaders.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/manifest_loader/loaders.py b/manifest_loader/loaders.py index 34930b2..eec1e4f 100644 --- a/manifest_loader/loaders.py +++ b/manifest_loader/loaders.py @@ -36,7 +36,7 @@ def get_single_match(manifest, key): 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 + pattern = split_pattern[1] if len(split_pattern) == 2 else split_pattern[0] files = manifest.get(parent, {}) if isinstance(files, dict): @@ -45,8 +45,7 @@ def get_multi_match(manifest, 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 [file for file in files if fnmatch.fnmatch(file, pattern)] return [] From 1eeac65611273fb17fde1d8f434e9f4267162b98 Mon Sep 17 00:00:00 2001 From: Elliot Charney Date: Wed, 30 Dec 2020 19:23:58 -0800 Subject: [PATCH 4/4] update docs for loaders --- docs/conf.py | 6 +- docs/docs/advanced_usage.md | 126 ++++++++++++++++++++++++++++++++++++ docs/docs/loaders.md | 23 +++++++ docs/docs/usage.md | 123 ----------------------------------- docs/index.rst | 4 +- 5 files changed, 157 insertions(+), 125 deletions(-) create mode 100644 docs/docs/advanced_usage.md create mode 100644 docs/docs/loaders.md diff --git a/docs/conf.py b/docs/conf.py index 3fc935c..a253b6f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -58,4 +58,8 @@ '.rst': 'restructuredtext', '.txt': 'markdown', '.md': 'markdown', -} \ No newline at end of file +} + +html_theme_options = { + "collapse_navigation" : False +} diff --git a/docs/docs/advanced_usage.md b/docs/docs/advanced_usage.md new file mode 100644 index 0000000..12d5a43 --- /dev/null +++ b/docs/docs/advanced_usage.md @@ -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' '' %}`, 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 %} + + +``` + +Will output as: + +```html + +``` + + +# 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 +``` diff --git a/docs/docs/loaders.md b/docs/docs/loaders.md new file mode 100644 index 0000000..33b994c --- /dev/null +++ b/docs/docs/loaders.md @@ -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._ + diff --git a/docs/docs/usage.md b/docs/docs/usage.md index 5494c5f..a900557 100644 --- a/docs/docs/usage.md +++ b/docs/docs/usage.md @@ -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' '' %}`, 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 %} - - -``` - -Will output as: - -```html - -``` - - -# 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 -``` diff --git a/docs/index.rst b/docs/index.rst index ccd3c94..aa3468d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -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