Repository: getnikola/nikola

Issue:
Make it possible to use SASS/LESS with bundles
via @padelt on IRC.

This could require magic, as we need to have a way to link the .less/sass/scss file with the resulting .css file.  [file_dep](https://github.com/getnikola/nikola/blob/master/nikola/plugins/task/bundles.py#L92) works on input files, while [build_bundle](https://github.com/getnikola/nikola/blob/master/nikola/plugins/task/bundles.py#L68) works on output files.



Discussion:
Well, we can depend on output files, no problem, it has the practical effect of chaining the tasks.

Doing that would also simplify code.css bundles.

Reading this ... I vaguely remember we used to depend on output files and we changed it to input files for what, at the time, seemed very good reasons.

Bundles already depend on output files:

``` python
                file_dep = [os.path.join(kw['output_folder'], fname)
                            for fname in files if
                            utils.get_asset_path(fname, self.site.THEMES, self.site.config['FILES_FOLDERS'])
                            or fname == 'assets/css/code.css']
```

So, I think this is not a real bug: You can use sass/less and then bundle the output files.

Has anyone ever _tried_ this? If yes, what failed? Is it just that the order of tasks is wrong? In that case it's trivial to fix using dependency injection now.

I'm affraid it is not working...
The culprit is the filter `utils.get_asset_path(fname, self.site.THEMES, self.site.config['FILES_FOLDERS'])` that looks for files on the disk in the "input" folders (THEMES and FILE_FOLDERS). The files generated by sass, however, only exists in the output folder.

Ok. But can I get a failure here? What _happens_, what errors appear, what's wrong with the generated output?

I can't see any obvious error.

Here is the layout of my theme (full repo: https://github.com/bcarlin/bcarlin.net/tree/nikola7) :

```
themes/bcarlin
...
├── bundles
├── parent
├── sass
│   ├── bcarlin.scss
│   └── targets
└── templates
    ├── base.tmpl
    ├── index.tmpl
    ├── list_post.tmpl
    ├── post.tmpl
    └── tag.tmpl
```

I've added the line `assets/css/foo.css=bcarlin.css` to my bundles file.

Here is the ouput of "nikola build" ([full output](https://gist.github.com/bcarlin/e24ed9e7610e3681c8d7#file-build_output-txt)) :

```
[18:18:43] Progs/bcarlin/bcarlin.net [.venv]±(nikola7|…) ➜  nikola build -a
Scanning posts..done!
[...]
.  prepare_sass_sources:cache/sass/targets
.  prepare_sass_sources:cache/sass/bcarlin.scss
.  build_sass:output/assets/css/bcarlin.css
.  create_bundles:output/assets/css/foo.css
.  create_bundles:output/assets/js/all-nocdn.js
.  create_bundles:output/assets/js/all.js
.  create_bundles:output/assets/css/all-nocdn.css
.  create_bundles:output/assets/css/all.css
.  sitemap:output/sitemap.xml
.  sitemap:output/sitemapindex.xml
.  robots_file:output/robots.txt
```

So the scss sources are compiled, the bundle "foo.css" is generated, but it is empty (whereas bcarlin.scss and output/assets/css/bcarlin.css have content).

When I add a print after file_dep is computed for each bundle :

``` python
file_dep = [os.path.join(kw['output_folder'], fname)
                            for fname in files if
                            utils.get_asset_path(fname, self.site.THEMES, self.site.config['FILES_FOLDERS'])
                            or fname == 'assets/css/code.css']
print("file_dep for bundle ", name, ": ", file_dep)
```

Here is what I get :

```
$ nikola build   
Scanning posts..done!
file_dep for bundle  assets/css/all-nocdn.css :  ['output/assets/css/bootstrap.css', 'output/assets/css/rst.css', 'output/assets/css/code.css', 'output/assets/css/colorbox.css', 'output/assets/css/theme.css']
file_dep for bundle  assets/js/all-nocdn.js :  ['output/assets/js/jquery.min.js', 'output/assets/js/bootstrap.min.js', 'output/assets/js/jquery.colorbox-min.js', 'output/assets/js/moment-with-locales.min.js', 'output/assets/js/fancydates.js']
file_dep for bundle  assets/css/all.css :  ['output/assets/css/rst.css', 'output/assets/css/code.css', 'output/assets/css/colorbox.css', 'output/assets/css/theme.css']
file_dep for bundle  assets/js/all.js :  ['output/assets/js/jquery.colorbox-min.js', 'output/assets/js/moment-with-locales.min.js', 'output/assets/js/fancydates.js']
file_dep for bundle  assets/css/foo.css :  []
.  render_archive:output/blog/index.html
[...]
.  render_tags:output/blog/tag/debian.xml
.  prepare_sass_sources:cache/sass/targets
.  prepare_sass_sources:cache/sass/bcarlin.scss
.  build_sass:output/assets/css/bcarlin.css
.  create_bundles:output/assets/css/all-nocdn.css
.  create_bundles:output/assets/css/all.css
.  create_bundles:output/assets/js/all-nocdn.js
.  create_bundles:output/assets/css/foo.css
.  create_bundles:output/assets/js/all.js
.  sitemap:output/sitemap.xml
.  sitemap:output/sitemapindex.xml
.  robots_file:output/robots.txt
```

Tell me if you need more information.


Resolving patch:
diff --git a/nikola/plugins/task/bundles.py b/nikola/plugins/task/bundles.py
--- a/nikola/plugins/task/bundles.py
+++ b/nikola/plugins/task/bundles.py
@@ -101,7 +101,11 @@ def build_bundle(output, inputs):
                     files.append(os.path.join(dname, fname))
                 file_dep = [os.path.join(kw['output_folder'], fname)
                             for fname in files if
-                            utils.get_asset_path(fname, self.site.THEMES, self.site.config['FILES_FOLDERS']) or fname == os.path.join('assets', 'css', 'code.css')]
+                            utils.get_asset_path(
+                                fname,
+                                self.site.THEMES,
+                                self.site.config['FILES_FOLDERS'],
+                                output_dir=kw['output_folder']) or fname == os.path.join('assets', 'css', 'code.css')]
                 # code.css will be generated by us if it does not exist in
                 # FILES_FOLDERS or theme assets.  It is guaranteed that the
                 # generation will happen before this task.
diff --git a/nikola/utils.py b/nikola/utils.py
--- a/nikola/utils.py
+++ b/nikola/utils.py
@@ -968,12 +968,14 @@ def get_crumbs(path, is_file=False, index_folder=None):
     return list(reversed(_crumbs))
 
 
-def get_asset_path(path, themes, files_folders={'files': ''}, _themes_dir='themes'):
+def get_asset_path(path, themes, files_folders={'files': ''}, _themes_dir='themes', output_dir='output'):
     """Return the "real", absolute path to the asset.
 
     By default, it checks which theme provides the asset.
     If the asset is not provided by a theme, then it will be checked for
     in the FILES_FOLDERS.
+    If it's not provided by either, it will be chacked in output, where
+    it may have been created by another plugin.
 
     >>> print(get_asset_path('assets/css/rst.css', ['bootstrap3', 'base']))
     /.../nikola/data/themes/base/assets/css/rst.css
@@ -1000,6 +1002,10 @@ def get_asset_path(path, themes, files_folders={'files': ''}, _themes_dir='theme
         if os.path.isfile(candidate):
             return candidate
 
+    candidate = os.path.join(output_dir, path)
+    if os.path.isfile(candidate):
+        return candidate
+
     # whatever!
     return None
 


Test patch: