Sass, Less, & More¶
Precompilers can be also be triggered by editor plugins and watcher processes. Use whatever method works best for your workflow.
DMP can automate compilation of your Sass and Less files–plus many others, from Stylus to Coffeescript to Typescript to Transcrypt.
When links(self)
is called by your template, these providers compare your source file timestamps against their output files. If an update is needed, the appropriate compiler is run.
During development, this check is done every time the template is rendered. During production, this check is done the first time the template is rendered. If you update your files, you need to bounce the server (we assume you’ve done compiling beforehand anyway).
First, be sure you’ve installed Sass or Less and that you can run them from the command line.
Detailed Options¶
Specify the comiler comand, source location, and target location in settings. DMP merges CONTENT_PROVIDERS
in your settings file wuth its defaults, so you can override a single option or many options. The following is the full set of options for these providers:
TEMPLATES = [
{
'NAME': 'django_mako_plus',
'BACKEND': 'django_mako_plus.MakoTemplates',
'OPTIONS': {
'CONTENT_PROVIDERS': [
# this should always be listed first
{ 'provider': 'django_mako_plus.JsContextProvider' },
# Sass precompiler provider
{ 'provider': 'django_mako_plus.CompileScssProvider',
'group': 'styles',
'enabled': True,
'sourcepath': None,
'targetpath': None,
'command': [],
},
# Less precompiler provider
{ 'provider': 'django_mako_plus.CompileLessProvider',
'group': 'styles',
'enabled': True,
'sourcepath': None,
'targetpath': None,
'command': [],
},
# Generic provider for any language
{ 'provider': 'django_mako_plus.CompileProvider',
'group': 'styles',
'enabled': True,
'sourcepath': None,
'targetpath': None,
'command': [],
},
# link providers should be listed AFTER compile providers
# see DMP docs /static_links.html for these provider options
{ 'provider': 'django_mako_plus.CssLinkProvider' },
{ 'provider': 'django_mako_plus.JsLinkProvider' },
],
},
},
]
Primary options:
sourcepath: | Specifies the search path DMP uses to find the source file (.scss, .less, etc.). The possible values are:
|
---|---|
targetpath: | Specifies the output path for the generated file. The possible values are:
|
command: | A list to be sent to Python’s [ shutil.which('sass'),
'--source-map',
'--load-path={}'.format(settings.BASE_DIR),
self.sourcepath,
self.targetpath,
]
|
Less used options:
group: | Allows you to separate the printing of links into two or more groups. For example, if you need half the providers to run at the top of your template and half at the bottom, you could specify two groups: “top” and “bottom”. To run only the top links, include this: ${ django_mako_plus.links(self, group="top") } . |
---|---|
enabled: | Specifies whether a provider is enabled or disabled (skipped). For example, if you specify 'enabled': DEBUG , a provider will run during development but be skipped at production. |
Example: Sass Files¶
Suppose we want to modify the output name of the Sass compile provider. Instead of generating filenames with <template>.css
, we want output names to be <template>.scss.css
. We’ll locate the .scss
source files and the .scss.css
generated files in the styles/
directory (the DMP convention).
What’s the reason behind this example? The.scss.css
extension makes it easy differentiate between generated CSS files and which are regular non-generated CSS files. This is just one way among many to differentiate.
In the following setup, note the changes to 1) the Sass compile provider’s output name, and 2) the CSS link provider’s search filepath.
TEMPLATES = [
{
'NAME': 'django_mako_plus',
'BACKEND': 'django_mako_plus.MakoTemplates',
'OPTIONS': {
'CONTENT_PROVIDERS': [
{ 'provider': 'django_mako_plus.JsContextProvider' },
{ 'provider': 'django_mako_plus.CompileScssProvider',
'sourcepath': lambda p: os.path.join(p.app_config.name, 'styles', p.template_relpath + '.scss'),
'targetpath': lambda p: os.path.join(p.app_config.name, 'styles', p.template_relpath + '.scss.css'),
# if you need to override the default command:
# 'command': lambda p: [ 'sass', f'--load-path="{BASE_DIR}"', p.sourcepath, p.targetpath ],
},
{ 'provider': 'django_mako_plus.CssLinkProvider',
'filepath': lambda p: os.path.join(p.app_config.name, 'styles', p.template_relpath + '.scss.css'),
},
{ 'provider': 'django_mako_plus.JsLinkProvider' },
],
},
},
]
By specifying the paths with lambda functions, we can use the following attributes of the provider objects:
p.template_name
- the name of the template, without extensionp.template_relpath
- the path of the template, relative to theapp/templates
directory. This is usually the same astemplate_name
, but it can be different if in a subdir of templates (e.g.homepage/templates/forms/login.html
->forms/login
.p.template_ext
- the extension of the template filenamep.app_config
- the AppConfig the template resides inp.app_config.name
- the name of the appp.template
- the Mako template objectp.template.filename
- full path to template filep.options
- the options for this provider (defaults + settings.py)p.provider_run.uid
- the unique context id (needed when creating links)p.provider_run.request
- the request object
Hints:
- Be sure DMP’s logging is set to “DEBUG” level (in settings). Then check the server logs; DMP’s providers print a lot of useful information to help you troubleshoot. The file paths printed should be especially useful.
- If the command is failing, you can copy the exact command being run from your server logs. Try running this command manually at a new terminal.
- Open the browser source (not the parsed DOM in the console, but the actual content being sent from the server). Inspect the link elements and paths for problems.
Example: Less files in dist/¶
Suppose we want to compile Less files to the dist/
folder under our project root. The following are examples of the file paths we want:
homepage/styles/index.scss
compiles todist/css/homepage.index.css
account/styles/login.scss
compiles todist/css/account.login.css
In the following setup, note the changes to 1) the Sass compile provider’s output name, and 2) the CSS link provider’s search filepath.
TEMPLATES = [
{
'NAME': 'django_mako_plus',
'BACKEND': 'django_mako_plus.MakoTemplates',
'OPTIONS': {
'CONTENT_PROVIDERS': [
{ 'provider': 'django_mako_plus.JsContextProvider' },
{ 'provider': 'django_mako_plus.CompileLessProvider',
'sourcepath': lambda p: os.path.join(p.app_config.name, 'styles', p.template_relpath + '.less'),
'targetpath': lambda p: os.path.join('dist', f'{p.app_config.name}.{p.template_relpath}.css'),
# if you need to override the default command:
# 'command': lambda p: [ 'lessc', '--source-map', p.sourcepath, p.targetpath ],
},
{ 'provider': 'django_mako_plus.CssLinkProvider',
'filepath': lambda p: os.path.join('dist', f'{p.app_config.name}.{p.template_relpath}.css'),
},
{ 'provider': 'django_mako_plus.JsLinkProvider' },
],
},
},
]
By specifying the paths with lambda functions, we can use the following attributes of the provider objects:
p.template_name
- the name of the template, without extensionp.template_relpath
- the path of the template, relative to theapp/templates
directory. This is usually the same astemplate_name
, but it can be different if in a subdir of templates (e.g.homepage/templates/forms/login.html
->forms/login
.p.template_ext
- the extension of the template filenamep.app_config
- the AppConfig the template resides inp.app_config.name
- the name of the appp.template
- the Mako template objectp.template.filename
- full path to template filep.options
- the options for this provider (defaults + settings.py)p.provider_run.uid
- the unique context id (needed when creating links)p.provider_run.request
- the request object
Hints:
- Be sure DMP’s logging is set to “DEBUG” level (in settings). Then check the server logs; DMP’s providers print a lot of useful information to help you troubleshoot. The file paths printed should be especially useful.
- If the command is failing, you can copy the exact command being run from your server logs. Try running this command manually at a new terminal.
- Open the browser source (not the parsed DOM in the console, but the actual content being sent from the server). Inspect the link elements and paths for problems.
Example: Transcrypt¶
Transcrypt is a library that transpiles Python code into Javascript. It lets us write browser scripts in our favorite language rather than that other one…
Modern web development practices would usually transpile using a bundler like webpack
. But there may be situations when you want DMP to trigger compilation, and DMP’s generic compile provider makes it easy.
The following settings automatically transcrypt <app>/scripts/*.py
files.
TEMPLATES = [
{
'NAME': 'django_mako_plus',
'BACKEND': 'django_mako_plus.MakoTemplates',
'OPTIONS': {
'CONTENT_PROVIDERS': [
# regular providers: context, JS, and CSS files
{ 'provider': 'django_mako_plus.JsContextProvider' },
{ 'provider': 'django_mako_plus.JsLinkProvider' },
{ 'provider': 'django_mako_plus.CssLinkProvider' },
# transcrypt app/scripts/*.py files: compiler + linker
{ 'provider': 'django_mako_plus.CompileProvider',
'sourcepath': lambda p: os.path.join(p.app_config.name, 'scripts', p.template_relpath + '.py'),
'targetpath': lambda p: os.path.join(p.app_config.name, 'scripts', '__target__', p.template_relpath + '.js'),
'command': lambda p: [
shutil.which('transcrypt'),
'--map',
'--build',
'--nomin',
os.path.join(BASE_DIR, p.app_config.name, 'scripts', p.template_relpath + '.py'),
],
},
{ 'provider': 'django_mako_plus.JsLinkProvider',
'filepath': lambda p: os.path.join(p.app_config.name, 'scripts', '__target__', p.template_relpath + '.js'),
'link_attrs': {
'type': 'module',
'async': 'true',
},
},
]
},
}
]
A few notes about these options:
- Regular CSS and Javascript files are still included (first three providers).
targetpath
contains the reference to./__target__
because Transcrypt always outputs to that directory.shutil.which
is similar to running/usr/bin/env
on Unix. It searches the system path for the given program.- The final
JsLinkProvider
adds the module attribute because Transcrypt outputs JS modules, and browsers requiretype="module"
for module scripts.