Merge branch 'dev' of github.com:Riverside-Healthcare/djLint into dev

This commit is contained in:
Christopher Pickering 2022-12-15 10:32:12 -06:00
commit ddb3da04b5
No known key found for this signature in database
GPG key ID: E14DB3B0A0FACF84
42 changed files with 8245 additions and 6491 deletions

21
.github/workflows/linkcheck.yaml vendored Normal file
View file

@ -0,0 +1,21 @@
name: Linkcheck
on: [push]
jobs:
linkcheck:
name: Linkcheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18.x
- name: npm install, build
run: |
cd docs
npm install
npm run build
- uses: untitaker/hyperlink@0.1.26
with:
args: docs/_site/

View file

@ -26,6 +26,27 @@ jobs:
- name: Setup Poetry
uses: snok/install-poetry@v1
- name: Update wheels in docs demo
run: |
# remove old wheels
rm -f docs/src/static/py/*
# build new wheels
poetry run pip wheel . -w docs/src/static/py
# rename wheels
cd docs/src/static/py
mv djlint* djlint-99-py3-none-any.whl
mv click* click-99-py3-none-any.whl
mv colorama* colorama-99-py3-none-any.whl
mv cssbeautifier* cssbeautifier-99-py3-none-any.whl
mv EditorConfig* EditorConfig-99-py3-none-any.whl
mv html_tag_names* html_tag_names-99-py3-none-any.whl
mv html_void_elements* html_void_elements-99-py3-none-any.whl
mv importlib_metadata* importlib_metadata-99-py3-none-any.whl
mv jsbeautifier* jsbeautifier-99-py3-none-any.whl
mv pathspec* pathspec-99-py3-none-any.whl
mv PyYAML* PyYAML-99-py3-none-any.whl
mv zipp* zipp-99-py3-none-any.whl
- name: Semantic Release
uses: cycjimmy/semantic-release-action@v3
@ -42,6 +63,6 @@ jobs:
- name: Build and publish package
if: steps.semantic.outputs.new_release_published == 'true'
uses: JRubics/poetry-publish@v1.13
uses: JRubics/poetry-publish@v1.15
with:
pypi_token: ${{ secrets.PYPI_API_TOKEN }}

View file

@ -5,13 +5,13 @@ exclude: >
)
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- repo: https://github.com/myint/autoflake
rev: v1.7.7
rev: v2.0.0
hooks:
- id: autoflake
exclude: &fixtures tests/functional/|tests/input|tests/extensions/data|tests/regrtest_data/|tests/data/
@ -22,7 +22,7 @@ repos:
- --remove-duplicate-keys
- --remove-unused-variables
- repo: https://github.com/asottile/pyupgrade
rev: v3.2.0
rev: v3.3.1
hooks:
- id: pyupgrade
args: [--py36-plus]
@ -33,11 +33,11 @@ repos:
exclude: docs*
additional_dependencies: [toml]
- repo: https://github.com/psf/black
rev: 22.10.0
rev: 22.12.0
hooks:
- id: black
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
rev: v2.4.0
rev: v2.5.0
hooks:
- id: pretty-format-ini
args: [--autofix]

View file

@ -9,7 +9,7 @@
"prepareCmd" : "poetry version ${nextRelease.version}"
}],
["@semantic-release/git", {
"assets": ["package.json", "pyproject.toml"]
"assets": ["package.json", "pyproject.toml", "docs"]
}],
"@semantic-release/github"
]

View file

@ -11,6 +11,8 @@ const outdent = require('outdent');
const schema = require('@quasibit/eleventy-plugin-schema');
const editOnGithub = require('eleventy-plugin-edit-on-github');
const i18n_func = require('eleventy-plugin-i18n/i18n.js');
const rollupper = require('./src/_utils/rollupper');
const { nodeResolve } = require('@rollup/plugin-node-resolve');
const slugifyCustom = (s) =>
slugify(s, { lower: true, remove: /[*+~.()'"!:@]/g });
@ -93,6 +95,15 @@ module.exports = function (eleventyConfig) {
eleventyConfig.addPlugin(metagen);
eleventyConfig.addPlugin(criticalCss);
eleventyConfig.addPlugin(schema);
eleventyConfig.addPlugin(rollupper, {
rollup: {
output: {
format: 'umd',
dir: '_site/static/js',
},
plugins: [nodeResolve()],
},
});
eleventyConfig.addPlugin(editOnGithub, {
// required
github_edit_repo: 'https://github.com/Riverside-Healthcare/djLint',
@ -190,6 +201,16 @@ module.exports = function (eleventyConfig) {
'src/static/img/favicon.ico': 'favicon.ico',
});
// copy wheels
eleventyConfig.addPassthroughCopy({
'src/static/py': 'static/py',
});
// copy python
eleventyConfig.addPassthroughCopy({
'src/static/js/worker.js': 'static/js/worker.js',
});
eleventyConfig.addFilter('jsonify', (text) => {
return JSON.stringify(text).replace(/(?:\\n\s*){2,}/g, '\\n');
});
@ -203,6 +224,15 @@ module.exports = function (eleventyConfig) {
}
});
eleventyConfig.addFilter('year', (value) => {
try {
const options = { year: 'numeric' };
return value.toLocaleDateString('en-us', options);
} catch (e) {
return value;
}
});
eleventyConfig.addFilter('algExcerpt', (text) => {
return text
.replace(/<code class="language-.*?">.*?<\/code>/gs, '')
@ -244,6 +274,8 @@ module.exports = function (eleventyConfig) {
'infinity',
'download',
'code-commit',
'spinner',
'circle-question',
],
},
'_site/static/font/fontawesome/webfonts',

11185
docs/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"name": "djlint_docs",
"version": "1.0.42",
"version": "1.0.51",
"description": "",
"main": "index.js",
"scripts": {
@ -13,23 +13,28 @@
"license": "AGPL-3.0-or-later",
"browserslist": "> 2%, not dead",
"dependencies": {
"@codemirror/lang-html": "^6.2.0",
"@codemirror/state": "^6.1.4",
"@creativebulma/bulma-divider": "^1.1.0",
"@fontsource/crimson-pro": "^4.5.8",
"@fontsource/rasa": "^4.5.8",
"@quasibit/eleventy-plugin-schema": "^1.9.1",
"@rollup/plugin-node-resolve": "^15.0.1",
"@sindresorhus/slugify": "^2.1.0",
"animate-sass": "^0.8.2",
"animate.css": "github:animate-css/animate.css",
"codemirror": "^6.0.1",
"eleventy-critical-css": "^1.1.0",
"eleventy-plugin-i18n": "^0.1.3",
"md5": "^2.3.0"
"md5": "^2.3.0",
"rollup": "^3.3.0"
},
"devDependencies": {
"@11ty/eleventy": "1.0.2",
"@11ty/eleventy-img": "2.0.1",
"@11ty/eleventy-plugin-syntaxhighlight": "4.2.0",
"@fontsource/inter": "4.5.14",
"@fortawesome/fontawesome-free": "6.2.0",
"@fortawesome/fontawesome-free": "6.2.1",
"@fullhuman/postcss-purgecss": "5.0.0",
"@toycode/markdown-it-class": "1.2.4",
"algoliasearch": "4.14.2",
@ -40,8 +45,8 @@
"cz-conventional-changelog": "3.3.0",
"eleventy-plugin-edit-on-github": "1.1.0",
"eleventy-plugin-metagen": "1.7.11",
"esbuild": "0.15.13",
"eslint": "8.26.0",
"esbuild": "0.16.3",
"eslint": "8.29.0",
"eslint-config-airbnb-base": "15.0.0",
"eslint-plugin-import": "2.26.0",
"fontawesome-subset": "4.3.1",
@ -51,11 +56,11 @@
"markdown-it-div": "1.1.0",
"markdown-it-imsize": "2.0.1",
"outdent": "0.8.0",
"postcss-cli": "10.0.0",
"postcss-cli": "10.1.0",
"postcss-nested": "6.0.0",
"prettier": "2.7.1",
"prettier": "2.8.1",
"prismjs": "1.29.0",
"sass": "1.56.0",
"sass": "1.56.2",
"slugify": "1.6.5"
},
"config": {

17
docs/readme.md Normal file
View file

@ -0,0 +1,17 @@
# djLint Docs
Docs are made with [11ty](https://www.11ty.dev).
## Running the docs website locally
1. change into the docs dir. `cd docs`
2. install node deps. `npm install`
3. turn on the website. `npm start`
## How the demo works
The demo is running python as a webworker in web assembly from [pyodide](https://pyodide.org/en/stable/index.html).
When the page is access a webworker starts, downloads python, installs djLint and deps (notice the wheels in `/src/static/py` that are updated when a new release is created.).
Once the worker responds "ready" the web UI is shown.

View file

@ -135,4 +135,20 @@ module.exports = {
ru: 'Игнорирование Контент',
fr: 'Ignorer le Contenu',
},
try_online: {
'en-US': '🤩 Try Online',
fr: '🤩 Essayez Ici',
ru: '🤩 Попробуйте онлайн',
},
demo_description: {
'en-US': 'Online HTML Template formatter using djLint!',
ru: 'Онлайн форматировщик HTML-шаблонов с использованием djLint',
fr: 'Formateur de modèles HTML en ligne utilisant djLint !',
},
footer_credits: {
'en-US':
'djLint was originally created for use in the <a href="https://atlas.bi" target="_blank">Atlas</a> projects by the <a href="https://github.com/riverside-Healthcare/" target="_blank">Riverside Healthcare Analytics</a> team.',
fr: 'djLint a été créé à l\'origine pour être utilisé dans les projets <a href="https://atlas.bi" target="_blank">Atlas</a> par l\'équipe <a href="https://github.com/riverside-Healthcare/" target="_blank">Riverside Healthcare Analytics</a>.',
ru: 'Изначально djLint был создан для использования в проектах <a href="https://atlas.bi" target="_blank">Atlas</a> командой <a href="https://github.com/riverside-Healthcare/" target="_blank">Riverside Healthcare Analytics</a>.',
},
};

154
docs/src/_includes/demo.njk Normal file
View file

@ -0,0 +1,154 @@
---
layout: layout.njk
date: Last Modified
description: Online HTML Template formatter. Reformat your HTML templates online with djLint.
keywords: online template formatter, djLint, HTML, templates, formatter, linter, rules, online html template formatter
---
<div class="container is-fluid pb-5">
<hr class="mt-0" />
<h1 class="title is-3">{{ "demo_description" | i18n | safe }}</h1>
<div class="block" id="djlint-status">
<span class="icon-text">
<span class="icon">
<i class="fas fa-spinner fa-spin"></i>
</span>
<span><strong>Loading djLint ..</strong></span>
</span>
</div>
<div class="columns is-hidden">
<div class="column is-narrow">
<form id="djlint-settings">
<div class="field">
<label class="label">Custom Blocks <a href="/docs/configuration/#custom_blocks" target="_blank"><span class="icon has-text-grey-light">
<i class="fas fa-circle-question"></i>
</span></a></label>
<div class="control">
<input id="settings-custom-blocks" class="input" type="text" placeholder="toc,example">
</div>
</div>
<div class="field">
<label class="label">Custom HTML <a href="/docs/configuration/#custom_html" target="_blank"><span class="icon has-text-grey-light">
<i class="fas fa-circle-question"></i>
</span></a></label>
<div class="control">
<input id="settings-custom-html" class="input" type="text" placeholder="mjml,simple-greeting,mj-\w+">
</div>
</div>
<div class="field">
<label class="label">Indent <a href="/docs/configuration/#indent" target="_blank"><span class="icon has-text-grey-light">
<i class="fas fa-circle-question"></i>
</span></a></label>
<div class="control">
<input id="settings-indent" class="input" type="text" placeholder="4">
</div>
</div>
<div class="field">
<label class="label">Blank Line after Tag <a href="/docs/configuration/#blank_line_after_tag" target="_blank"><span class="icon has-text-grey-light">
<i class="fas fa-circle-question"></i>
</span></a></label>
<div class="control">
<input id="settings-blank-line-after-tag" class="input" type="text" placeholder="load,extends,include">
</div>
</div>
<div class="field">
<label class="label">Blank Line before Tag <a href="/docs/configuration/#blank_line_before_tag" target="_blank"><span class="icon has-text-grey-light">
<i class="fas fa-circle-question"></i>
</span></a></label>
<div class="control">
<input id="settings-blank-line-before-tag" class="input" type="text" placeholder="load,extends,include">
</div>
</div>
<div class="field">
<label class="label">Profile <a href="/docs/configuration/#profile" target="_blank"><span class="icon has-text-grey-light">
<i class="fas fa-circle-question"></i>
</span></a></label>
<div class="control">
<div class="select">
<select id="settings-profile" >
<option value="html">html (default)</option>
<option value="django">django</option>
<option value="jinja">jinja</option>
<option value="nunjucks">nunjucks (nunjucks and twig)</option>
<option value="handlebars">handlebars (handlebars and mustache)</option>
<option value="golang">golang</option>
<option value="angular">angular</option>
</select>
</div>
</div>
</div>
<div class="field">
<label class="label">Max Line Length <a href="/docs/configuration/#max_line_length" target="_blank"><span class="icon has-text-grey-light">
<i class="fas fa-circle-question"></i>
</span></a></label>
<div class="control">
<input id="settings-max-line-length" class="input" type="text" placeholder="80">
</div>
</div>
<div class="field">
<label class="label">Max Attribute Length <a href="/docs/configuration/#max_attribute_length" target="_blank"><span class="icon has-text-grey-light">
<i class="fas fa-circle-question"></i>
</span></a></label>
<div class="control">
<input id="settings-max-attribute-length" class="input" type="text" placeholder="0">
</div>
</div>
<div class="field">
<label class="checkbox">
<input id="settings-format-attribute-template-tags" type="checkbox">
Format Attribute Template Tags <a href="/docs/configuration/#format_attribute_template_tags" target="_blank"><span class="icon has-text-grey-light">
<i class="fas fa-circle-question"></i>
</span></a>
</label>
</div>
<div class="field">
<label class="checkbox">
<input id="settings-preserve-leading-space" type="checkbox">
Preserve Leading Space <a href="/docs/configuration/#preserve_leading_space" target="_blank"><span class="icon has-text-grey-light">
<i class="fas fa-circle-question"></i>
</span></a>
</label>
</div>
<div class="field">
<label class="checkbox">
<input id="settings-preserve-blank-space" type="checkbox">
Preserve Blank Space <a href="/docs/configuration/#preserve_blank_lines" target="_blank"><span class="icon has-text-grey-light">
<i class="fas fa-circle-question"></i>
</span></a>
</label>
</div>
<div class="field">
<label class="checkbox">
<input id="settings-format-js" type="checkbox">
Format JS <a href="/docs/configuration/#format_js" target="_blank"><span class="icon has-text-grey-light">
<i class="fas fa-circle-question"></i>
</span></a>
</label>
</div>
<div class="field">
<label class="checkbox">
<input id="settings-format-css" type="checkbox">
Format CSS <a href="/docs/configuration/#format_css" target="_blank"><span class="icon has-text-grey-light">
<i class="fas fa-circle-question"></i>
</span></a>
</label>
</div>
</form>
</div>
<div class="column" id="djlint-input">
</div>
<div class="column" id="djlint-output">
</div>
</div>
</div>
{% rollup "src/static/js/editor.js" | url %}

View file

@ -12,7 +12,10 @@
<div class="column"></div>
<div class="column">
<p class="copyright has-text-grey-dark has-text-right">
djLint | ©2021 Riverside Healthcare
djLint | ©{{ page.date | year }} Riverside Healthcare
</p>
<p class="has-text-grey-dark has-text-right mt-2">
<small>{{ "footer_credits" | i18n | safe }}</small>
</p>
</div>
</div>

View file

@ -34,6 +34,9 @@
<a class="navbar-item has-text-grey-dark umami--nav--bestpractices" href="{{ "lang_code_url" | i18n }}/docs/best-practices">
{{ "best_practices" | i18n }}
</a>
<a class="navbar-item has-text-grey-dark umami--nav--tryonline" href="{{ "lang_code_url" | i18n }}/demo">
{{ "try_online" | i18n }}
</a>
</div>
<div class="navbar-end mx-0">
<a class="navbar-item button is-white animated fadeIn umami--nav--github"

View file

@ -0,0 +1,91 @@
/*
rollup plugin from https://www.hoeser.dev/blog/2021-02-28-11ty-and-rollup/
*/
const rollup = require('rollup');
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
module.exports = (eleventyConfig, options) => {
new Rollupper(eleventyConfig, options);
};
class Rollupper {
inputFiles = {};
rollupOptions = {};
constructor(eleventyConfig, { shortcode = 'rollup', rollup } = {}) {
this.rollupOptions = rollup;
eleventyConfig.on('beforeBuild', () => this.beforeBuild());
eleventyConfig.on('afterBuild', () => this.afterBuild());
// We want to use "this" in the callback function, so we save the class instance beforehand
const thisRollupper = this;
eleventyConfig.addAsyncShortcode(shortcode, function (...args) {
return thisRollupper.rollupperShortcode(this, ...args);
});
}
beforeBuild() {
this.inputFiles = {};
}
async rollupperShortcode(eleventyInstance, src, fileRelative = false) {
// Resolve to the correct relative location
if (fileRelative) {
src = path.join(path.dirname(eleventyInstance.page.inputPath), src);
}
// resolve to absolute, since rollup uses absolute paths
src = path.resolve(src);
// generate a unique name for the file.
// we take the first 6 chars of the sha256 of the absolute paths.
const fileHash = await new Promise(function (resolve, reject) {
const hash = crypto.createHash('sha256');
const input = fs.createReadStream(src);
input.on('error', reject);
input.on('data', function (chunk) {
hash.update(chunk);
});
input.on('close', function () {
resolve(hash.digest('hex'));
});
});
const scriptSrc = fileHash.substr(0, 6) + '.js';
// register for rollup bundling
this.inputFiles[src] = scriptSrc;
// calculate script src after bundling
const relativePath = path.relative(
eleventyInstance.page.outputPath,
path.join(this.rollupOptions.output.dir, scriptSrc),
);
return `<script src="${relativePath}" type="module"></script>`;
}
async afterBuild() {
// Return early if no JS was used, since rollup throws on empty inputs
if (!Object.keys(this.inputFiles).length) {
return;
}
const bundle = await rollup.rollup({
input: Object.keys(this.inputFiles),
...this.rollupOptions,
});
const inputFiles = this.inputFiles;
await bundle.write({
entryFileNames: (chunk) => {
return inputFiles[chunk.facadeModuleId];
},
...this.rollupOptions.output,
});
await bundle.close();
}
}

4
docs/src/demo.njk Normal file
View file

@ -0,0 +1,4 @@
---
layout: demo.njk
date: Last Modified
---

4
docs/src/fr/demo.njk Normal file
View file

@ -0,0 +1,4 @@
---
layout: demo.njk
date: Last Modified
---

4
docs/src/ru/demo.njk Normal file
View file

@ -0,0 +1,4 @@
---
layout: demo.njk
date: Last Modified
---

View file

@ -380,7 +380,7 @@ pre .number {
background-color: unset;
}
pre {
pre:not(.CodeMirror-line) {
margin-bottom: 1.5rem !important;
}

View file

@ -5,6 +5,7 @@
@import '../../../../node_modules/@fortawesome/fontawesome-free/scss/_mixins';
@import '../../../../node_modules/@fortawesome/fontawesome-free/scss/_core';
@import '../../../../node_modules/@fortawesome/fontawesome-free/scss/_sizing';
@import '../../../../node_modules/@fortawesome/fontawesome-free/scss/_animated';
$fa-font-path: '/static/font/fontawesome/webfonts';
@font-face {
@ -66,6 +67,8 @@ $icons: (
circle-arrow-right: $fa-var-circle-arrow-right,
globe: $fa-var-globe,
code-commit: $fa-var-code-commit,
spinner: $fa-var-spinner,
circle-question: $fa-var-circle-question,
);
@each $key, $value in $icons {

View file

@ -0,0 +1,167 @@
import { EditorView, basicSetup } from 'codemirror';
import { EditorState, Compartment } from '@codemirror/state';
import { html } from '@codemirror/lang-html';
let session_id = 0;
if (typeof Worker !== 'undefined') {
console.log('creating web worker ');
let w;
if (typeof w == 'undefined') {
w = new Worker('/static/js/worker.js');
}
function getConfig() {
let config = {};
const customBlocks = document.getElementById(
'settings-custom-blocks',
).value;
if (customBlocks) config['customBlocks'] = customBlocks;
const customHtml = document.getElementById('settings-custom-html').value;
if (customHtml) config['customHtml'] = customHtml;
const indent = document.getElementById('settings-indent').value;
if (indent) config['indent'] = indent;
const blankLineAfterTag = document.getElementById(
'settings-blank-line-after-tag',
).value;
if (blankLineAfterTag) config['blankLineAfterTag'] = blankLineAfterTag;
const blankLineBeforeTag = document.getElementById(
'settings-blank-line-before-tag',
).value;
if (blankLineBeforeTag) config['blankLineBeforeTag'] = blankLineBeforeTag;
const profile = document.getElementById('settings-profile').value;
if (profile) config['profile'] = profile;
const maxLineLength = document.getElementById(
'settings-max-line-length',
).value;
if (maxLineLength) config['maxLineLength'] = maxLineLength;
const maxAttributeLength = document.getElementById(
'settings-max-attribute-length',
).value;
if (maxAttributeLength) config['maxAttributeLength'] = maxAttributeLength;
const formatAttributeTemplateTags = document.getElementById(
'settings-format-attribute-template-tags',
).checked;
if (formatAttributeTemplateTags)
config['formatAttributeTemplateTags'] = formatAttributeTemplateTags;
const preserveLeadingSpace = document.getElementById(
'settings-preserve-leading-space',
).checked;
if (preserveLeadingSpace)
config['preserveLeadingSpace'] = preserveLeadingSpace;
const preserveBlankSpace = document.getElementById(
'settings-preserve-blank-space',
).checked;
if (preserveBlankSpace) config['preserveBlankSpace'] = preserveBlankSpace;
const formatJs = document.getElementById('settings-format-js').checked;
if (formatJs) config['formatJs'] = formatJs;
const formatCss = document.getElementById('settings-format-css').checked;
if (formatCss) config['formatCss'] = formatCss;
return config;
}
const runPython = (script) => {
session_id += 1;
w.postMessage({
config: getConfig(),
html: script,
id: session_id,
});
};
w.onmessage = (event) => {
const { id, type, message, ...data } = event.data;
if (type == 'status') {
const div = document.createElement('div');
div.innerText = message;
const status = document.getElementById('djlint-status');
status.insertBefore(div, status.lastElementChild);
if (message === 'ready') {
document.getElementById('djlint-status').classList.add('is-hidden');
document
.getElementById('djlint-settings')
.closest('.columns.is-hidden')
.classList.remove('is-hidden');
runPython(editor.state.doc.toString());
}
}
if ((type == 'error' || type == 'html') && id == session_id) {
setOutput(message);
} else {
console.log(event.data);
}
};
let timer;
let editor = new EditorView({
state: EditorState.create({
extensions: [
basicSetup,
html(),
EditorView.updateListener.of((v) => {
if (v.docChanged) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
runPython(v.state.doc.toString());
}, 100);
}
}),
],
doc: `<div>\n <p>Welcome to djLint online!</p>\n</div>`,
}),
parent: document.getElementById('djlint-input'),
});
let output = new EditorView({
state: EditorState.create({
extensions: [basicSetup, html()],
doc: ``,
readonly: true,
}),
parent: document.getElementById('djlint-output'),
});
// add pyodide returned value to the output
function setOutput(stdout) {
const currentValue = output.state.doc.toString();
const endPosition = currentValue.length;
output.dispatch({
changes: {
from: 0,
to: endPosition,
insert: stdout,
},
});
}
function setInput(stdout) {
const currentValue = editor.state.doc.toString();
const endPosition = currentValue.length;
editor.dispatch({
changes: {
from: 0,
to: endPosition,
insert: stdout,
},
});
}
document.getElementById('djlint-settings').addEventListener('change', () => {
runPython(editor.state.doc.toString());
});
} else {
// Sorry! No Web Worker support..
document
.getElementById('djlint-status')
.innerText(
'Sorry, a browser that supports web workers is required to use this online tool.',
);
}
// pass the editor value to the pyodide.runPython function and show the result in the output section
// document.getElementById("djlint-settings").addEventListener("change", () => {evaluatePython()})

View file

@ -0,0 +1,143 @@
importScripts('https://cdn.jsdelivr.net/pyodide/v0.21.3/full/pyodide.js');
function capitalize(raw_word) {
const word = raw_word.toString();
return word.charAt(0).toUpperCase() + word.slice(1);
}
async function loadPyodideAndPackages() {
const origin = location.origin;
self.pyodide = await loadPyodide();
// build wheels with pip wheel .
await self.pyodide.loadPackage([
`${origin}/static/py/djlint-99-py3-none-any.whl`,
`${origin}/static/py/click-99-py3-none-any.whl`,
`${origin}/static/py/colorama-99-py3-none-any.whl`,
`${origin}/static/py/cssbeautifier-99-py3-none-any.whl`,
`${origin}/static/py/EditorConfig-99-py3-none-any.whl`,
`${origin}/static/py/html_tag_names-99-py3-none-any.whl`,
`${origin}/static/py/html_void_elements-99-py3-none-any.whl`,
`${origin}/static/py/importlib_metadata-99-py3-none-any.whl`,
`${origin}/static/py/jsbeautifier-99-py3-none-any.whl`,
`${origin}/static/py/pathspec-99-py3-none-any.whl`,
`${origin}/static/py/PyYAML-99-py3-none-any.whl`,
`${origin}/static/py/zipp-99-py3-none-any.whl`,
]);
postMessage({
type: 'status',
message:
'Installing djlint, click, colorama, cssbeautifier, editorconfig, html_tag_names, html_void_elements, importlib_metadata, jsbeautifier, pathspec, pyyaml, zipp ..',
});
await self.pyodide.loadPackage('regex');
postMessage({ type: 'status', message: 'Installing regex..' });
await self.pyodide.loadPackage('six');
postMessage({ type: 'status', message: 'Installing six..' });
await self.pyodide.loadPackage('tomli');
postMessage({ type: 'status', message: 'Installing tomli..' });
await self.pyodide.loadPackage('tqdm');
postMessage({ type: 'status', message: 'Installing tqdm..' });
postMessage({
type: 'status',
message:
'Running Python ' +
pyodide.runPython(`
import sys
sys.version
`),
});
postMessage({ type: 'status', message: 'ready' });
return pyodide;
}
let pyodideReadyPromise = loadPyodideAndPackages();
self.onmessage = async (event) => {
await pyodideReadyPromise;
const { id, config, html } = event.data;
const profile = config.profile ? `\n,profile="${config.profile}"` : '';
const indent = config.indent ? `\n,indent=${config.indent}` : '';
const preserveLeadingSpace = config.preserveLeadingSpace
? `\n,preserve_leading_space=${capitalize(config.preserveLeadingSpace)}`
: '';
const preserveBlankSpace = config.preserveBlankSpace
? `\n,preserve_blank_lines=${capitalize(config.preserveBlankSpace)}`
: '';
const formatJs = config.formatJs
? `\n,format_js=${capitalize(config.formatJs)}`
: '';
const formatCss = config.formatCss
? `\n,format_css=${capitalize(config.formatCss)}`
: '';
const customBlocks = config.customBlocks
? `config.custom_blocks="${config.customBlocks}"`
: '';
const customHtml = config.customHtml
? `config.custom_html="${config.customHtml}"`
: '';
const maxLineLength = config.maxLineLength
? `config.max_line_length=${config.maxLineLength}`
: '';
const maxAttributeLength = config.maxAttributeLength
? `config.max_attribute_length=${config.maxAttributeLength}`
: '';
const formatAttributeTemplateTags = config.formatAttributeTemplateTags
? `config.format_attribute_template_tags=${capitalize(
config.formatAttributeTemplateTags,
)}`
: '';
const blankLineAfterTag = config.blankLineAfterTag
? `config.blank_line_after_tag="${config.blankLineAfterTag}"`
: '';
const blankLineBeforeTag = config.blankLineBeforeTag
? `config.blank_line_before_tag="${config.blankLineBeforeTag}"`
: '';
try {
await self.pyodide.runPythonAsync(`
import io
import os
sys.modules['_multiprocessing'] = object
from multiprocessing.pool import ThreadPool
sys.stdout = io.StringIO()
from pathlib import Path
from djlint.reformat import reformat_file
from djlint.settings import Config
import tempfile
`);
await self.pyodide.runPythonAsync('sys.stdout.flush()');
await pyodide.runPythonAsync(`
temp_file = tempfile.NamedTemporaryFile(delete=False)
temp_file.write(str.encode("""${html}"""))
temp_file.seek(0)
config = Config(
temp_file.name${indent}${profile}${preserveLeadingSpace}${preserveBlankSpace}${formatJs}${formatCss}
)
${customBlocks}
${customHtml}
${maxLineLength}
${maxAttributeLength}
${formatAttributeTemplateTags}
${blankLineAfterTag}
${blankLineBeforeTag}
print(Path(list(reformat_file(config, Path(temp_file.name)).keys())[0]).read_text().rstrip())
temp_file.close()
os.unlink(temp_file.name)
`);
let stdout = await self.pyodide.runPythonAsync('sys.stdout.getvalue()');
await self.pyodide.runPythonAsync('sys.stdout.flush()');
postMessage({ type: 'html', message: stdout, id: id });
} catch (err) {
self.postMessage({ type: 'error', message: err.message, id: id });
}
};

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

1453
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"name": "djlint",
"version": "1.19.4",
"version": "1.19.12",
"description": "HTML Template Linter and Formatter",
"main": "./bin/index.js",
"directories": {
@ -56,17 +56,17 @@
"yargs": "17.6.2"
},
"devDependencies": {
"@semantic-release/changelog": "6.0.1",
"@semantic-release/changelog": "6.0.2",
"@semantic-release/commit-analyzer": "9.0.2",
"@semantic-release/exec": "6.0.3",
"@semantic-release/git": "10.0.1",
"@semantic-release/github": "8.0.6",
"@semantic-release/github": "8.0.7",
"@semantic-release/npm": "9.0.1",
"@semantic-release/release-notes-generator": "10.0.3",
"cz-conventional-changelog": "3.3.0",
"lint-staged": "13.0.3",
"lint-staged": "13.1.0",
"semantic-release": "19.0.5",
"xo": "0.52.4"
"xo": "0.53.1"
},
"config": {
"commitizen": {

1336
poetry.lock generated

File diff suppressed because it is too large Load diff

View file

@ -7,7 +7,7 @@ build-backend = "poetry.core.masonry.api"
[tool.poetry]
name="djlint"
version="1.19.4"
version="1.19.7"
description="HTML Template Linter and Formatter"
license="GPL-3.0-or-later"
authors=["Christopher Pickering <cpickering@rhc.net>"]
@ -48,10 +48,10 @@ pytest = "^7.1.2"
pytest-cov = "^4.0.0"
pytest-xdist = "^3.0.0"
pylint = "^2.14.5"
mypy = "^0.982"
mypy = "^0.991"
types-PyYAML = "^6.0.11"
pep8-naming = "^0.13.1"
tox = "^3.25.1"
tox = "^4.0.0"
[tool.poetry.scripts]
djlint = "djlint:main"

View file

@ -135,14 +135,14 @@
message: (Django) Internal links should use the {% url ... %} pattern.
flags: re.DOTALL|re.I
patterns:
- <(?:a|div|span|input)\b[^>]*?\s(?:href|data-url|data-src|action)=[\"|'](?!(?:https?://)|javascript:|on\w+:|mailto:|tel:)[\w|/]+
- <(?:a|div|span|input)\b[^>]*?\s(?:href|data-url|data-src|action)=[\"|'](?!(?:https?://)|javascript:|on\w+:|mailto:|tel:|data:)[\w|/]+
- <form\s+?[^>]*?(?:action)=[\"|'](?!(?:https?://)|javascript:|on\w+:|mailto:|tel:)[\w|/]+
- rule:
name: J018
message: (Jinja) Internal links should use the {{ url_for() ... }} pattern.
flags: re.DOTALL|re.I
patterns:
- <(?:a|div|span|input)\b[^>]*?\s(?:href|data-url|data-src|action)=[\"|'](?!(?:https?://)|javascript:|on\w+:|mailto:|tel:)[\w|/]+
- <(?:a|div|span|input)\b[^>]*?\s(?:href|data-url|data-src|action)=[\"|'](?!(?:https?://)|javascript:|on\w+:|mailto:|tel:|data:)[\w|/]+
- <form\s+?[^>]*?(?:action)=[\"|'](?!(?:https?://)|javascript:|on\w+:|mailto:|tel:)[\w|/]+
- rule:
name: H019

View file

@ -417,13 +417,29 @@ def test_DJ018(runner: CliRunner, tmp_file: TextIO) -> None:
result = runner.invoke(djlint, [tmp_file.name, "--profile", "jinja"])
assert "J018 1:" in result.output
# test mailto:
# test mailto:, tel:
write_to_file(
tmp_file.name,
b'<a href="mailto:joe"></a><a href="tel:joe"></a>',
)
result = runner.invoke(djlint, [tmp_file.name])
result = runner.invoke(djlint, [tmp_file.name, "--profile", "django"])
assert result.exit_code == 0
assert "D018" not in result.output
result = runner.invoke(djlint, [tmp_file.name, "--profile", "jinja"])
assert result.exit_code == 0
assert "J018" not in result.output
# test data:
write_to_file(
tmp_file.name,
b'<a href="data:,Hello%2C%20World%21"></a>',
)
result = runner.invoke(djlint, [tmp_file.name, "--profile", "django"])
assert result.exit_code == 0
assert "D018" not in result.output
result = runner.invoke(djlint, [tmp_file.name, "--profile", "jinja"])
assert result.exit_code == 0
assert "J018" not in result.output
# test attribute names
write_to_file(