feat(formatter): added options to format css and js using jsbeautifier

This commit is contained in:
Christopher Pickering 2022-07-28 13:58:03 -05:00
parent 2f8533f190
commit 99b5ee2c1f
No known key found for this signature in database
GPG key ID: E14DB3B0A0FACF84
19 changed files with 332 additions and 51 deletions

View file

@ -127,6 +127,16 @@ function run(stdin) {
describe: 'Use .gitignore file to extend excludes.',
type: 'boolean',
demandOption: false,
})
.option('format-css', {
describe: 'Also format contents of <style> tags.',
type: 'boolean',
demandOption: false,
})
.option('format-js', {
describe: 'Also format contents of <script> tags.',
type: 'boolean',
demandOption: false,
}).argv;
// Set flags
@ -142,6 +152,8 @@ function run(stdin) {
const use_gitignore = options['use-gitignore']
? '--use-gitignore'
: undefined;
const format_css = options['format-css'] ? '--format-css' : undefined;
const format_js = options['format-js'] ? '--format-js' : undefined;
const has_stdin = stdin === '' ? options._[0] : '-';
// Set variables
@ -164,6 +176,8 @@ function run(stdin) {
indent,
profile,
ignore,
format_css,
format_js,
].filter((x) => {
return x !== undefined;
});

View file

@ -340,5 +340,41 @@
"value": "\"per-file-ignores\": {\n \"file.html\": \"H026,H025\",\n \"file_two.html\":\"H001\"\n }"
}
]
},
{
"name": "format_js",
"description": {
"en": "Format contents of `script` tags using `js-beautify`. See [js-beautify](https://github.com/beautify-web/js-beautify/blob/main/python/jsbeautifier/javascript/options.py) for all configuration options. Template syntax is not [fully supported](https://github.com/beautify-web/js-beautify/issues) in supported.",
"ru": "Форматирование содержимого тегов `script` с помощью `js-beautify`. Все параметры конфигурации см. в [js-beautify](https://github.com/beautify-web/js-beautify/blob/main/python/jsbeautifier/javascript/options.py). Синтаксис шаблона не [полностью поддерживается](https://github.com/beautify-web/js-beautify/issues) в поддерживается.",
"fr": "Formate le contenu des balises `script` en utilisant `js-beautify`. Voir [js-beautify](https://github.com/beautify-web/js-beautify/blob/main/python/jsbeautifier/javascript/options.py) pour toutes les options de configuration. La syntaxe des modèles n'est pas [entièrement prise en charge] (https://github.com/beautify-web/js-beautify/issues) dans pris en charge."
},
"usage": [
{
"name": "pyproject.toml",
"value": "[tool.djlint]\nformat_js=true\n\n[tool.djlint.js]\nindent_size=5\n"
},
{
"name": ".djlintrc",
"value": "\"format_js\": true\n\"js\": {\n \"indent_size\": 5\n }"
}
]
},
{
"name": "format_css",
"description": {
"en": "Format contents of `script` tags using `css-beautify`. See [css-beautify](https://github.com/beautify-web/js-beautify/blob/main/python/cssbeautifier/css/options.py) for all configuration options. Template syntax is not [fully supported](https://github.com/beautify-web/js-beautify/issues) in supported.",
"ru": "Форматирование содержимого тегов `script` с помощью `css-beautify`. Все параметры конфигурации см. в [css-beautify](https://github.com/beautify-web/js-beautify/blob/main/python/cssbeautifier/css/options.py). Синтаксис шаблона не [полностью поддерживается](https://github.com/beautify-web/js-beautify/issues) в поддерживается.",
"fr": "Formate le contenu des balises `script` en utilisant `css-beautify`. Voir [css-beautify](https://github.com/beautify-web/js-beautify/blob/main/python/cssbeautifier/css/options.py) pour toutes les options de configuration. La syntaxe des modèles n'est pas [entièrement prise en charge] (https://github.com/beautify-web/js-beautify/issues) dans pris en charge."
},
"usage": [
{
"name": "pyproject.toml",
"value": "[tool.djlint]\nformat_css=true\n\n[tool.djlint.css]\nindent_size=5\n"
},
{
"name": ".djlintrc",
"value": "\"format_css\": true\n\"css\": {\n \"indent_size\": 5\n }"
}
]
}
]

View file

@ -20,6 +20,9 @@ To format the code and update files run:
```bash
djlint . --reformat
# how about formatting scripts and styles?
djlint . --reformat --format-css --format-js
```
<div class="box notification is-info is-light">

View file

@ -30,24 +30,26 @@ Usage: djlint [OPTIONS] SRC ...
djLint · lint and reformat HTML templates.
Options:
--version Show the version and exit.
-e, --extension TEXT File extension to check [default: html]
-i, --ignore TEXT Codes to ignore. ex: "H014,H017"
--reformat Reformat the file(s).
--check Check formatting on the file(s).
--indent INTEGER Indent spacing. [default: 4]
--quiet Do not print diff when reformatting.
--profile TEXT Enable defaults by template language. ops: django,
jinja, nunjucks, handlebars, golang, angular,
html [default: html]
--require-pragma Only format or lint files that starts with a comment
with the text 'djlint:on'
--lint Lint for common issues. [default option]
--use-gitignore Use .gitignore file to extend excludes.
--warn Return errors as warnings.
--version Show the version and exit.
-e, --extension TEXT File extension to check [default: html]
-i, --ignore TEXT Codes to ignore. ex: "H014,H017"
--reformat Reformat the file(s).
--check Check formatting on the file(s).
--indent INTEGER Indent spacing. [default: 4]
--quiet Do not print diff when reformatting.
--profile TEXT Enable defaults by template language. ops: django,
jinja, nunjucks, handlebars, golang, angular,
html [default: html]
--require-pragma Only format or lint files that starts with a comment
with the text 'djlint:on'
--lint Lint for common issues. [default option]
--use-gitignore Use .gitignore file to extend excludes.
--warn Return errors as warnings.
--preserve-leading-space Attempt to preserve leading space on text.
--preserve-blank-lines Attempt to preserve blank lines.
-h, --help Show this message and exit.
--format-css Also format contents of <style> tags.
--format-js Also format contents of <script> tags.
-h, --help Show this message and exit.
```
{% admonition

View file

@ -20,6 +20,9 @@ Pour formater le code et exécuter les fichiers de mise à jour :
```bash
djlint . --reformat
# qu'en est-il des scripts et des styles de mise en forme ?
djlint . --reformat --format-css --format-js
```
<div class="box notification is-info is-light">

View file

@ -30,24 +30,26 @@ Usage: djlint [OPTIONS] SRC ...
djLint · lint and reformat HTML templates.
Options:
--version Show the version and exit.
-e, --extension TEXT File extension to check [default: html]
-i, --ignore TEXT Codes to ignore. ex: "H014,H017"
--reformat Reformat the file(s).
--check Check formatting on the file(s).
--indent INTEGER Indent spacing. [default: 4]
--quiet Do not print diff when reformatting.
--profile TEXT Enable defaults by template language. ops: django,
jinja, nunjucks, handlebars, golang, angular,
html [default: html]
--require-pragma Only format or lint files that starts with a comment
with the text 'djlint:on'
--lint Lint for common issues. [default option]
--use-gitignore Use .gitignore file to extend excludes.
--warn Return errors as warnings.
--version Show the version and exit.
-e, --extension TEXT File extension to check [default: html]
-i, --ignore TEXT Codes to ignore. ex: "H014,H017"
--reformat Reformat the file(s).
--check Check formatting on the file(s).
--indent INTEGER Indent spacing. [default: 4]
--quiet Do not print diff when reformatting.
--profile TEXT Enable defaults by template language. ops: django,
jinja, nunjucks, handlebars, golang, angular,
html [default: html]
--require-pragma Only format or lint files that starts with a comment
with the text 'djlint:on'
--lint Lint for common issues. [default option]
--use-gitignore Use .gitignore file to extend excludes.
--warn Return errors as warnings.
--preserve-leading-space Attempt to preserve leading space on text.
--preserve-blank-lines Attempt to preserve blank lines.
-h, --help Show this message and exit.
--format-css Also format contents of <style> tags.
--format-js Also format contents of <script> tags.
-h, --help Show this message and exit.
```
{% admonition

View file

@ -20,6 +20,9 @@ djlint . --check
```bash
djlint . --reformat
# как насчет скриптов и стилей форматирования?
djlint . --reformat --format-css --format-js
```
<div class="box notification is-info is-light">

View file

@ -30,24 +30,26 @@ Usage: djlint [OPTIONS] SRC ...
djLint · lint and reformat HTML templates.
Options:
--version Show the version and exit.
-e, --extension TEXT File extension to check [default: html]
-i, --ignore TEXT Codes to ignore. ex: "H014,H017"
--reformat Reformat the file(s).
--check Check formatting on the file(s).
--indent INTEGER Indent spacing. [default: 4]
--quiet Do not print diff when reformatting.
--profile TEXT Enable defaults by template language. ops: django,
jinja, nunjucks, handlebars, golang, angular,
html [default: html]
--require-pragma Only format or lint files that starts with a comment
with the text 'djlint:on'
--lint Lint for common issues. [default option]
--use-gitignore Use .gitignore file to extend excludes.
--warn Return errors as warnings.
--version Show the version and exit.
-e, --extension TEXT File extension to check [default: html]
-i, --ignore TEXT Codes to ignore. ex: "H014,H017"
--reformat Reformat the file(s).
--check Check formatting on the file(s).
--indent INTEGER Indent spacing. [default: 4]
--quiet Do not print diff when reformatting.
--profile TEXT Enable defaults by template language. ops: django,
jinja, nunjucks, handlebars, golang, angular,
html [default: html]
--require-pragma Only format or lint files that starts with a comment
with the text 'djlint:on'
--lint Lint for common issues. [default option]
--use-gitignore Use .gitignore file to extend excludes.
--warn Return errors as warnings.
--preserve-leading-space Attempt to preserve leading space on text.
--preserve-blank-lines Attempt to preserve blank lines.
-h, --help Show this message and exit.
--format-css Also format contents of <style> tags.
--format-js Also format contents of <script> tags.
-h, --help Show this message and exit.
```
{% admonition

57
poetry.lock generated
View file

@ -77,6 +77,27 @@ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.1
[package.extras]
toml = ["tomli"]
[[package]]
name = "cssbeautifier"
version = "1.14.4"
description = "CSS unobfuscator and beautifier."
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
editorconfig = ">=0.12.2"
jsbeautifier = "*"
six = ">=1.13.0"
[[package]]
name = "editorconfig"
version = "0.12.3"
description = "EditorConfig File Locator and Interpreter for Python"
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "execnet"
version = "1.9.0"
@ -143,6 +164,18 @@ requirements_deprecated_finder = ["pipreqs", "pip-api"]
colors = ["colorama (>=0.4.3,<0.5.0)"]
plugins = ["setuptools"]
[[package]]
name = "jsbeautifier"
version = "1.14.4"
description = "JavaScript unobfuscator and beautifier."
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
editorconfig = ">=0.12.2"
six = ">=1.13.0"
[[package]]
name = "mypy-extensions"
version = "0.4.3"
@ -299,6 +332,14 @@ category = "main"
optional = false
python-versions = "*"
[[package]]
name = "six"
version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "tomli"
version = "2.0.1"
@ -354,7 +395,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
[metadata]
lock-version = "1.1"
python-versions = "^3.7,<4.0"
content-hash = "9e46ab4d8e14c063f669ba48e3f0bb696067b5bdfc916679597e1449f1454eb9"
content-hash = "2c02767bc8b9ddc8b1b693082fc2660d2dbeb4d188b108499dd0e923628ef396"
[metadata.files]
atomicwrites = [
@ -441,6 +482,13 @@ coverage = [
{file = "coverage-6.4.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:e2618cb2cf5a7cc8d698306e42ebcacd02fb7ef8cfc18485c59394152c70be97"},
{file = "coverage-6.4.2.tar.gz", hash = "sha256:6c3ccfe89c36f3e5b9837b9ee507472310164f352c9fe332120b764c9d60adbe"},
]
cssbeautifier = [
{file = "cssbeautifier-1.14.4.tar.gz", hash = "sha256:107eb1408cd5770361f9044e107102aa1ec03f2a547908a37bf1777000d3b18b"},
]
editorconfig = [
{file = "EditorConfig-0.12.3-py3-none-any.whl", hash = "sha256:6b0851425aa875b08b16789ee0eeadbd4ab59666e9ebe728e526314c4a2e52c1"},
{file = "EditorConfig-0.12.3.tar.gz", hash = "sha256:57f8ce78afcba15c8b18d46b5170848c88d56fd38f05c2ec60dbbfcb8996e89e"},
]
execnet = [
{file = "execnet-1.9.0-py2.py3-none-any.whl", hash = "sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142"},
{file = "execnet-1.9.0.tar.gz", hash = "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5"},
@ -465,6 +513,9 @@ isort = [
{file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"},
{file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"},
]
jsbeautifier = [
{file = "jsbeautifier-1.14.4.tar.gz", hash = "sha256:729fa6c0fe935b2666f3fead7ec576f911ae6e8d7bdf578f9b2fb92ba377bbb3"},
]
mypy-extensions = [
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
@ -620,6 +671,10 @@ regex = [
{file = "regex-2022.1.18-cp39-cp39-win_amd64.whl", hash = "sha256:ebaeb93f90c0903233b11ce913a7cb8f6ee069158406e056f884854c737d2442"},
{file = "regex-2022.1.18.tar.gz", hash = "sha256:97f32dc03a8054a4c4a5ab5d761ed4861e828b2c200febd4e46857069a483916"},
]
six = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
tomli = [
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},

View file

@ -43,6 +43,8 @@ pathspec = "^0.9.0"
importlib-metadata = "^4.11.0"
html-void-elements = "^0.1.0"
html-tag-names = "^0.1.2"
jsbeautifier = "^1.14.4"
cssbeautifier = "^1.14.4"
[tool.poetry.dev-dependencies]
black = "^22.1.0"

View file

@ -104,6 +104,16 @@ from .src import get_src
is_flag=True,
help="Attempt to preserve blank lines.",
)
@click.option(
"--format-css",
is_flag=True,
help="Also format contents of <style> tags.",
)
@click.option(
"--format-js",
is_flag=True,
help="Also format contents of <script> tags.",
)
@colorama_text(autoreset=True)
def main(
src: List[str],
@ -120,6 +130,8 @@ def main(
warn: bool,
preserve_leading_space: bool,
preserve_blank_lines: bool,
format_css: bool,
format_js: bool,
) -> None:
"""djLint · HTML template linter and formatter."""
config = Config(
@ -137,6 +149,8 @@ def main(
warn=warn,
preserve_leading_space=preserve_leading_space,
preserve_blank_lines=preserve_blank_lines,
format_css=format_css,
format_js=format_js,
)
temp_file = None

View file

@ -0,0 +1,38 @@
"""djLint function to call cssbeautify."""
from functools import partial
import cssbeautifier
import regex as re
from jsbeautifier.javascript.options import BeautifierOptions
from ..settings import Config
def format_css(html: str, config: Config) -> str:
"""Format css inside <style> tags."""
def launch_formatter(config: Config, match: re.Match) -> str:
"""Add break after if not in ignored block."""
indent = len(match.group(1)) * " "
inner_indent = indent + config.indent
opts = BeautifierOptions(config.css_config)
beautified = (
"\n"
+ inner_indent
+ ("\n" + inner_indent).join(
cssbeautifier.beautify(match.group(3), opts).splitlines()
)
)
return match.group(1) + match.group(2) + beautified + "\n" + indent
func = partial(launch_formatter, config)
return re.sub(
re.compile(
r"([ ]*?)(<style\b.*?>)(.+?)(?=</style>)",
re.IGNORECASE | re.DOTALL,
),
func,
html,
)

View file

@ -0,0 +1,39 @@
"""djLint function to call jsbeautify."""
from functools import partial
import jsbeautifier
import regex as re
from jsbeautifier.javascript.options import BeautifierOptions
from ..settings import Config
def format_js(html: str, config: Config) -> str:
"""Format javascript inside <script> tags."""
def launch_formatter(config: Config, match: re.Match) -> str:
"""Add break after if not in ignored block."""
indent = len(match.group(1)) * " "
inner_indent = indent + config.indent
opts = BeautifierOptions(config.js_config)
beautified = (
"\n"
+ inner_indent
+ ("\n" + inner_indent).join(
jsbeautifier.beautify(match.group(3), opts).splitlines()
)
)
return match.group(1) + match.group(2) + beautified + "\n" + indent
func = partial(launch_formatter, config)
return re.sub(
re.compile(
r"([ ]*?)(<script\b.*?>)(.+?)(?=</script>)",
re.IGNORECASE | re.DOTALL,
),
func,
html,
)

View file

@ -8,8 +8,10 @@ from pathlib import Path
from .formatter.compress import compress_html
from .formatter.condense import condense_html
from .formatter.css import format_css
from .formatter.expand import expand_html
from .formatter.indent import indent_html
from .formatter.js import format_js
from .settings import Config
@ -23,9 +25,13 @@ def reformat_file(config: Config, this_file: Path) -> dict:
condensed = condense_html(expanded, config)
indented = indent_html(condensed, config)
beautified_code = indent_html(condensed, config)
beautified_code = indented
if config.format_css:
beautified_code = format_css(beautified_code, config)
if config.format_js:
beautified_code = format_js(beautified_code, config)
if config.check is not True:
# update the file

View file

@ -193,6 +193,8 @@ class Config:
warn: bool = False,
preserve_leading_space: bool = False,
preserve_blank_lines: bool = False,
format_css: bool = False,
format_js: bool = False,
):
self.reformat = reformat
@ -239,6 +241,13 @@ class Config:
"preserve_blank_lines", False
)
self.format_js: bool = format_js or djlint_settings.get("format_js", False)
self.js_config = djlint_settings.get("js")
self.css_config = djlint_settings.get("css")
self.format_css: bool = format_css or djlint_settings.get("format_css", False)
# ignore is based on input and also profile
self.ignore: str = str(ignore or djlint_settings.get("ignore", ""))

View file

@ -0,0 +1,17 @@
<div>
<script>
__jsnlog_configure = function(JL) {
JL.setOptions({
"defaultAjaxUrl": "/analytics/trace"
})
};
try {
__jsnlog_configure(JL);
} catch (e) {};
</script>
</div>
<style>
.body {
width: 12px
}
</style>

View file

@ -0,0 +1,9 @@
[tool.djlint]
format_js = true
format_css = true
[tool.djlint.js]
indent_size = 5
[tool.djlint.css]
indent_size = 3

View file

@ -0,0 +1,27 @@
"""Djlint tests specific to .djlintrc configuration.
run::
pytest tests/test_config/test_json/test_config.py --cov=src/djlint --cov-branch \
--cov-report xml:coverage.xml --cov-report term-missing
pytest tests/test_config/test_json/test_config.py::test_config
"""
# pylint: disable=C0116
from click.testing import CliRunner
from src.djlint import main as djlint
def test_config(runner: CliRunner) -> None:
result = runner.invoke(
djlint,
[
"tests/test_config/test_scripts_styles/html.html",
"--check",
],
)
print(result.output)
assert result.exit_code == 0