jlto 1.4.2 β†’ 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.nvmrc CHANGED
@@ -1 +1 @@
1
- v16
1
+ v24.13.1
package/README.md CHANGED
@@ -1,13 +1,3 @@
1
- # Support Ukraine πŸ‡ΊπŸ‡¦
2
-
3
- - Via United24 platform (the initiative of the President of Ukraine):
4
- - [One click donation (credit card, bank transfer or crypto)](https://u24.gov.ua/)
5
- - Via National Bank of Ukraine:
6
- - [Ukrainian army](https://bank.gov.ua/en/about/support-the-armed-forces)
7
- - [Humanitarian aid to Ukraine](https://bank.gov.ua/en/about/humanitarian-aid-to-ukraine)
8
-
9
- [#StandWithUkraine](https://twitter.com/hashtag/StandWithUkraine)
10
-
11
1
  # JLTO
12
2
 
13
3
  [![CircleCI](https://circleci.com/gh/dmytro-krekota/jlto.svg?style=svg)](https://app.circleci.com/pipelines/github/dmytro-krekota/jlto)
@@ -17,6 +7,9 @@
17
7
 
18
8
  > Jinja Like Templates Optimizer (JLTO) is a Nodejs-based tool for optimizing Jinja like templates.
19
9
 
10
+ If you are thinking about contributing to open source, this article is a good read:
11
+ [Why You Should Write Open Source Code and How It Helps Your Career](https://explainme.online/article/why-you-should-write-open-source-code-and-how-it-helps-your-career).
12
+
20
13
  **Gulp tool for JLTO:**
21
14
 
22
15
  [gulp-jlto](https://www.npmjs.com/package/gulp-jlto)
@@ -44,11 +37,13 @@
44
37
  - cleanupBlocks - flag for optimize blocks
45
38
  - cleanupExpressions - flag for optimize expressions
46
39
  - removeComments - flag for removing comments
47
- - minifyHtml - flag for minifying html code with [html-minifier](https://www.npmjs.com/package/html-minifier)
48
- - minifyHtmlOptions - options for html-minifier
40
+ - minifyHtml - flag for minifying html code with [html-minifier-next](https://www.npmjs.com/package/html-minifier-next)
41
+ - minifyHtmlOptions - options for html-minifier-next
49
42
 
50
43
  See default values for above options [here](https://github.com/dmytro-krekota/jlto/blob/master/lib/core/default.js).
51
44
 
45
+ `optimizeString` always returns a Promise, so use `await` (or `.then(...)`) for all calls.
46
+
52
47
  ## Usage
53
48
 
54
49
  **Simple example:**
@@ -60,13 +55,14 @@ let template = `
60
55
  {{ "<John & Paul> ?" | escape }}
61
56
  {{ '2.7' | round }}{% if product %}Product exists.{% endif %}
62
57
  `;
63
- let optimizedTemplate = jlto.optimizeString(template);
64
- // optimizedTemplate:
65
- // `
66
- //{{hello}}
67
- //{{"<John & Paul> ?"|escape}}
68
- //{{'2.7'|round}}{%if product%}Product exists.{%endif%}
69
- // `
58
+ jlto.optimizeString(template).then((optimizedTemplate) => {
59
+ // optimizedTemplate:
60
+ // `
61
+ //{{hello}}
62
+ //{{"<John & Paul> ?"|escape}}
63
+ //{{'2.7'|round}}{%if product%}Product exists.{%endif%}
64
+ // `
65
+ });
70
66
  ```
71
67
 
72
68
  **Example of using minifyHtml option:**
@@ -81,38 +77,76 @@ let template = `
81
77
  {% endfor %}
82
78
  </div>
83
79
  </div>`;
84
- let optimizedTemplate = jlto.optimizeString(template, {minifyHtml: true});
85
- // optimizedTemplate:
86
- // `<div {%if id%} id="{{id|escape('html_attr')}}" {%endif%} class="section-container {{classes|join(' ')|html_attribute}}"><div class="section-writables"> {%for writable in writables%} {{writable|write|raw}} {%endfor%} </div></div>`
80
+ jlto.optimizeString(template, {minifyHtml: true}).then((optimizedTemplate) => {
81
+ // optimizedTemplate:
82
+ // `<div {%if id%} id="{{id|escape('html_attr')}}" {%endif%} class="section-container {{classes|join(' ')|html_attribute}}"><div class="section-writables"> {%for writable in writables%} {{writable|write|raw}} {%endfor%} </div></div>`
83
+ });
87
84
  ```
88
85
 
89
86
  **Example of "nunjucks" templates minification with the custom GruntJS task:**
90
87
 
91
88
  ```js
92
89
  module.exports = (grunt) => {
93
- grunt.registerTask('min-nunjucks', 'Min nunjucks templates', () => {
90
+ grunt.registerTask('min-nunjucks', 'Min nunjucks templates', function () {
94
91
  let jlto = require('jlto');
95
92
  let fs = require('fs');
96
- let glob = require('glob');
93
+ let {glob} = require('glob');
97
94
  let done = this.async();
98
- glob('./**/*.nunjucks.html', (error, files) => {
99
- files.forEach((filePath) => {
95
+ (async () => {
96
+ let files = await glob('./**/*.nunjucks.html');
97
+ for (const filePath of files) {
100
98
  let fileContent;
101
99
  fileContent = fs.readFileSync(filePath).toString();
102
100
  try {
103
- fileContent = jlto.optimizeString(fileContent, {minifyHtml: true});
101
+ fileContent = await jlto.optimizeString(fileContent, {minifyHtml: true});
104
102
  fs.writeFileSync(filePath, fileContent);
105
103
  } catch (ignored) {}
106
- });
104
+ }
107
105
  return done();
108
- });
106
+ })().catch(() => done());
109
107
  });
110
108
  };
111
109
  ```
112
110
 
111
+ **Example of "nunjucks" templates minification with the custom webpack plugin:**
112
+
113
+ ```js
114
+ let jlto = require('jlto');
115
+ let webpack = require('webpack');
116
+
117
+ class MinNunjucksPlugin {
118
+ apply(compiler) {
119
+ compiler.hooks.thisCompilation.tap('MinNunjucksPlugin', (compilation) => {
120
+ compilation.hooks.processAssets.tapPromise(
121
+ {
122
+ name: 'MinNunjucksPlugin',
123
+ stage: webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE,
124
+ },
125
+ async (assets) => {
126
+ for (const filePath of Object.keys(assets)) {
127
+ if (!filePath.endsWith('.nunjucks.html')) {
128
+ continue;
129
+ }
130
+ let fileContent = compilation.getAsset(filePath).source.source().toString();
131
+ try {
132
+ fileContent = await jlto.optimizeString(fileContent, {minifyHtml: true});
133
+ compilation.updateAsset(filePath, new webpack.sources.RawSource(fileContent));
134
+ } catch (ignored) {}
135
+ }
136
+ },
137
+ );
138
+ });
139
+ }
140
+ }
141
+
142
+ module.exports = {
143
+ plugins: [new MinNunjucksPlugin()],
144
+ };
145
+ ```
146
+
113
147
  ## Tests
114
148
 
115
- Unit tests are written using Mocha and Chai. To run, invoke `npm test`.
149
+ Tests are written using Jest and Node's `assert` module. To run them, invoke `npm test`.
116
150
 
117
151
  ## License
118
152
 
@@ -0,0 +1,72 @@
1
+ const js = require('@eslint/js');
2
+ const globals = require('globals');
3
+ const eslintPluginPrettierRecommended = require('eslint-plugin-prettier/recommended');
4
+
5
+ module.exports = [
6
+ {
7
+ ignores: ['coverage/**', 'node_modules/**'],
8
+ },
9
+ js.configs.recommended,
10
+ eslintPluginPrettierRecommended,
11
+ {
12
+ languageOptions: {
13
+ ecmaVersion: 'latest',
14
+ sourceType: 'commonjs',
15
+ globals: {
16
+ ...globals.node,
17
+ ...globals.mocha,
18
+ },
19
+ },
20
+ rules: {
21
+ semi: 1,
22
+ 'no-mixed-spaces-and-tabs': 1,
23
+ 'no-trailing-spaces': 1,
24
+ 'space-infix-ops': 1,
25
+ quotes: [
26
+ 1,
27
+ 'single',
28
+ {
29
+ allowTemplateLiterals: true,
30
+ avoidEscape: true,
31
+ },
32
+ ],
33
+ 'no-unused-vars': 1,
34
+ 'one-var': [1, 'never'],
35
+ 'vars-on-top': 1,
36
+ 'no-undef': 1,
37
+ 'no-use-before-define': 1,
38
+ 'no-console': 0,
39
+ 'keyword-spacing': 1,
40
+ 'key-spacing': 1,
41
+ 'space-before-blocks': 1,
42
+ 'space-in-parens': 1,
43
+ indent: [1, 2],
44
+ camelcase: [
45
+ 1,
46
+ {
47
+ properties: 'always',
48
+ },
49
+ ],
50
+ 'comma-spacing': [
51
+ 1,
52
+ {
53
+ before: false,
54
+ after: true,
55
+ },
56
+ ],
57
+ 'comma-style': [1, 'last'],
58
+ 'newline-before-return': 1,
59
+ 'object-curly-spacing': [1, 'never'],
60
+ 'semi-spacing': [
61
+ 1,
62
+ {
63
+ before: false,
64
+ after: true,
65
+ },
66
+ ],
67
+ eqeqeq: 1,
68
+ 'linebreak-style': [1, 'unix'],
69
+ 'prettier/prettier': ['error'],
70
+ },
71
+ },
72
+ ];
@@ -1,73 +1,20 @@
1
+ let spaceCleanupHelper = require('./spaceCleanupHelper');
2
+
1
3
  /**
2
4
  * @class BlocksHelper
3
5
  */
4
6
  class BlocksHelper {
5
7
  constructor(options) {
6
8
  this.options = options;
7
- this.utils = require('./../core/utils');
8
- }
9
-
10
- getBlockWithoutExtraSpaces(expression) {
11
- let body = expression.slice(this.options.blockStart.length).slice(0, -this.options.blockEnd.length);
12
- let resultBody = '';
13
- let i;
14
- let waitForChar;
15
-
16
- body = body.trim();
17
- for (i = 0; i < body.length; i++) {
18
- if (waitForChar && body[i] !== waitForChar) {
19
- resultBody += body[i];
20
- continue;
21
- }
22
- if (body[i] === '"' || body[i] === "'") {
23
- if (!waitForChar) {
24
- waitForChar = body[i];
25
- } else {
26
- waitForChar = null;
27
- }
28
- }
29
- if (this.options.specialChars.indexOf(body[i]) >= 0) {
30
- resultBody = resultBody.trim();
31
- }
32
- if (this.options.specialChars.indexOf(resultBody[resultBody.length - 1]) >= 0 && body[i] === ' ') {
33
- continue;
34
- }
35
- if (resultBody[resultBody.length - 1] === ' ' && body[i] === ' ') {
36
- continue;
37
- }
38
- if (body[i] !== '\n') {
39
- resultBody += body[i];
40
- }
41
- }
42
-
43
- return this.options.blockStart + resultBody + this.options.blockEnd;
44
9
  }
45
10
 
46
11
  clearExtraSpaces(template) {
47
- let i;
48
- let startIndex = null;
49
- let tempSubstring;
50
- let resultString = '';
51
-
52
- for (i = 0; i < template.length + 1; i++) {
53
- if (!this.utils.isNull(startIndex)) {
54
- tempSubstring = template.substring(i - this.options.blockEnd.length, i);
55
- if (tempSubstring === this.options.blockEnd) {
56
- resultString += this.getBlockWithoutExtraSpaces(template.substring(startIndex, i));
57
- startIndex = null;
58
- }
59
- }
60
- if (this.utils.isNull(startIndex)) {
61
- tempSubstring = template.substring(i, i + this.options.blockStart.length);
62
- if (tempSubstring === this.options.blockStart) {
63
- startIndex = i;
64
- } else if (!this.utils.isUndefined(template[i])) {
65
- resultString += template[i];
66
- }
67
- }
68
- }
69
-
70
- return resultString;
12
+ return spaceCleanupHelper.clearTemplateExtraSpaces(
13
+ template,
14
+ this.options.blockStart,
15
+ this.options.blockEnd,
16
+ this.options.specialChars,
17
+ );
71
18
  }
72
19
  }
73
20
 
@@ -1,73 +1,20 @@
1
+ let spaceCleanupHelper = require('./spaceCleanupHelper');
2
+
1
3
  /**
2
4
  * @class ExpressionsHelper
3
5
  */
4
6
  class ExpressionsHelper {
5
7
  constructor(options) {
6
8
  this.options = options;
7
- this.utils = require('./../core/utils');
8
- }
9
-
10
- getExpressionWithoutExtraSpaces(expression) {
11
- let body = expression.slice(this.options.expressionStart.length).slice(0, -this.options.expressionEnd.length);
12
- let resultBody = '';
13
- let i;
14
- let waitForChar;
15
-
16
- body = body.trim();
17
- for (i = 0; i < body.length; i++) {
18
- if (waitForChar && body[i] !== waitForChar) {
19
- resultBody += body[i];
20
- continue;
21
- }
22
- if (body[i] === '"' || body[i] === "'") {
23
- if (!waitForChar) {
24
- waitForChar = body[i];
25
- } else {
26
- waitForChar = null;
27
- }
28
- }
29
- if (this.options.specialChars.indexOf(body[i]) >= 0) {
30
- resultBody = resultBody.trim();
31
- }
32
- if (this.options.specialChars.indexOf(resultBody[resultBody.length - 1]) >= 0 && body[i] === ' ') {
33
- continue;
34
- }
35
- if (resultBody[resultBody.length - 1] === ' ' && body[i] === ' ') {
36
- continue;
37
- }
38
- if (body[i] !== '\n') {
39
- resultBody += body[i];
40
- }
41
- }
42
-
43
- return this.options.expressionStart + resultBody + this.options.expressionEnd;
44
9
  }
45
10
 
46
11
  clearExtraSpaces(template) {
47
- let i;
48
- let startIndex = null;
49
- let tempSubstring;
50
- let resultString = '';
51
-
52
- for (i = 0; i < template.length + 1; i++) {
53
- if (!this.utils.isNull(startIndex)) {
54
- tempSubstring = template.substring(i - this.options.expressionEnd.length, i);
55
- if (tempSubstring === this.options.expressionEnd) {
56
- resultString += this.getExpressionWithoutExtraSpaces(template.substring(startIndex, i));
57
- startIndex = null;
58
- }
59
- }
60
- if (this.utils.isNull(startIndex)) {
61
- tempSubstring = template.substring(i, i + this.options.expressionStart.length);
62
- if (tempSubstring === this.options.expressionStart) {
63
- startIndex = i;
64
- } else if (!this.utils.isUndefined(template[i])) {
65
- resultString += template[i];
66
- }
67
- }
68
- }
69
-
70
- return resultString;
12
+ return spaceCleanupHelper.clearTemplateExtraSpaces(
13
+ template,
14
+ this.options.expressionStart,
15
+ this.options.expressionEnd,
16
+ this.options.specialChars,
17
+ );
71
18
  }
72
19
  }
73
20
 
@@ -0,0 +1,85 @@
1
+ let isEscaped = (text, index) => {
2
+ let backslashCount = 0;
3
+ let i;
4
+
5
+ for (i = index - 1; i >= 0 && text[i] === '\\'; i--) {
6
+ backslashCount++;
7
+ }
8
+
9
+ return backslashCount % 2 === 1;
10
+ };
11
+
12
+ let getTagWithoutExtraSpaces = (tag, startDelimiter, endDelimiter, specialChars) => {
13
+ let body = tag.slice(startDelimiter.length).slice(0, -endDelimiter.length);
14
+ let resultBody = '';
15
+ let i;
16
+ let waitForChar = null;
17
+ let specialCharSet = new Set(specialChars);
18
+
19
+ body = body.trim();
20
+ for (i = 0; i < body.length; i++) {
21
+ if (waitForChar && body[i] !== waitForChar) {
22
+ resultBody += body[i];
23
+ continue;
24
+ }
25
+
26
+ if (body[i] === '"' || body[i] === "'") {
27
+ if (!waitForChar) {
28
+ waitForChar = body[i];
29
+ } else if (waitForChar === body[i] && !isEscaped(body, i)) {
30
+ waitForChar = null;
31
+ }
32
+ }
33
+
34
+ if (specialCharSet.has(body[i])) {
35
+ resultBody = resultBody.trim();
36
+ }
37
+ if (specialCharSet.has(resultBody[resultBody.length - 1]) && body[i] === ' ') {
38
+ continue;
39
+ }
40
+ if (resultBody[resultBody.length - 1] === ' ' && body[i] === ' ') {
41
+ continue;
42
+ }
43
+ if (body[i] !== '\n') {
44
+ resultBody += body[i];
45
+ }
46
+ }
47
+
48
+ return startDelimiter + resultBody + endDelimiter;
49
+ };
50
+
51
+ let clearTemplateExtraSpaces = (template, startDelimiter, endDelimiter, specialChars) => {
52
+ let i;
53
+ let startIndex = null;
54
+ let tempSubstring;
55
+ let resultString = '';
56
+
57
+ for (i = 0; i < template.length + 1; i++) {
58
+ if (startIndex !== null) {
59
+ tempSubstring = template.substring(i - endDelimiter.length, i);
60
+ if (tempSubstring === endDelimiter) {
61
+ resultString += getTagWithoutExtraSpaces(
62
+ template.substring(startIndex, i),
63
+ startDelimiter,
64
+ endDelimiter,
65
+ specialChars,
66
+ );
67
+ startIndex = null;
68
+ }
69
+ }
70
+ if (startIndex === null) {
71
+ tempSubstring = template.substring(i, i + startDelimiter.length);
72
+ if (tempSubstring === startDelimiter) {
73
+ startIndex = i;
74
+ } else if (template[i] !== undefined) {
75
+ resultString += template[i];
76
+ }
77
+ }
78
+ }
79
+
80
+ return resultString;
81
+ };
82
+
83
+ module.exports = {
84
+ clearTemplateExtraSpaces,
85
+ };
package/lib/jlto.js CHANGED
@@ -1,37 +1,37 @@
1
+ let Default = require('./core/default');
2
+ let ExpressionsHelper = require('./helpers/expressionsHelper');
3
+ let BlocksHelper = require('./helpers/blocksHelper');
4
+ let CommentsHelper = require('./helpers/commentsHelper');
5
+ let minify = require('html-minifier-next').minify;
6
+
1
7
  /**
2
8
  * @class JLTO
3
9
  * @classdesc JLTO - Jinja Like Templates Optimizer.
4
10
  */
5
11
  class JLTO {
6
12
  constructor() {
7
- this.defaultOptions = new (require('./core/default'))().options;
13
+ this.defaultOptions = new Default().options;
8
14
  }
9
15
 
10
- optimizeString(string, options = {}) {
11
- let expressionsHelper;
12
- let blocksHelper;
13
- let commentsHelper;
14
- this.options = {...this.defaultOptions, ...options};
15
- expressionsHelper = new (require('./helpers/expressionsHelper'))(this.options);
16
- blocksHelper = new (require('./helpers/blocksHelper'))(this.options);
17
- commentsHelper = new (require('./helpers/commentsHelper'))(this.options);
16
+ async optimizeString(string, options = {}) {
17
+ let resolvedOptions = {...this.defaultOptions, ...options};
18
18
 
19
- if (this.options.removeComments) {
20
- string = commentsHelper.removeComments(string);
19
+ if (resolvedOptions.removeComments) {
20
+ string = new CommentsHelper(resolvedOptions).removeComments(string);
21
21
  }
22
- if (this.options.cleanupExpressions) {
23
- string = expressionsHelper.clearExtraSpaces(string);
22
+ if (resolvedOptions.cleanupExpressions) {
23
+ string = new ExpressionsHelper(resolvedOptions).clearExtraSpaces(string);
24
24
  }
25
- if (this.options.cleanupBlocks) {
26
- string = blocksHelper.clearExtraSpaces(string);
25
+ if (resolvedOptions.cleanupBlocks) {
26
+ string = new BlocksHelper(resolvedOptions).clearExtraSpaces(string);
27
27
  }
28
- if (this.options.minifyHtml) {
29
- let minify = require('html-minifier').minify;
30
- this.options.minifyHtmlOptions = {
28
+ if (resolvedOptions.minifyHtml) {
29
+ let minifyHtmlOptions = {
31
30
  ...this.defaultOptions.minifyHtmlOptions,
32
31
  ...(options.minifyHtmlOptions || {}),
33
32
  };
34
- string = minify(string, this.options.minifyHtmlOptions);
33
+
34
+ return minify(string, minifyHtmlOptions);
35
35
  }
36
36
 
37
37
  return string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jlto",
3
- "version": "1.4.2",
3
+ "version": "1.5.2",
4
4
  "description": "Nodejs-based tool for optimizing Jinja like templates",
5
5
  "keywords": [
6
6
  "jinja",
@@ -32,17 +32,15 @@
32
32
  "license": "MIT",
33
33
  "main": "index.js",
34
34
  "scripts": {
35
- "lint": "eslint test lib --max-warnings=0 --fix",
35
+ "lint": "eslint test lib --max-warnings=0",
36
+ "lint:fix": "eslint test lib --max-warnings=0 --fix",
36
37
  "test": "npm run lint && jest --coverage",
37
38
  "ci-testing": "jest --coverage && cat ./coverage/lcov.info | coveralls",
38
39
  "prettier": "prettier \"{test,lib}/**/*.js\" --write"
39
40
  },
40
- "engines": {
41
- "node": ">=16"
42
- },
43
41
  "pre-commit": [
44
42
  "prettier",
45
- "lint"
43
+ "lint:fix"
46
44
  ],
47
45
  "jest": {
48
46
  "testEnvironment": "node",
@@ -54,19 +52,23 @@
54
52
  ]
55
53
  },
56
54
  "devDependencies": {
57
- "chai": "4.3.6",
58
- "coveralls": "3.1.1",
59
- "eslint": "8.21.0",
60
- "eslint-config-prettier": "^8.5.0",
61
- "eslint-plugin-prettier": "^4.2.1",
62
- "jest": "28.1.3",
63
- "liquid": "5.1.1",
64
- "nunjucks": "3.2.3",
65
- "pre-commit": "1.2.2",
66
- "prettier": "2.7.1",
67
- "twig": "1.15.4"
55
+ "@eslint/js": "^9.39.3",
56
+ "coveralls": "^3.1.1",
57
+ "eslint": "^9.39.3",
58
+ "eslint-config-prettier": "^10.1.8",
59
+ "eslint-plugin-prettier": "^5.5.5",
60
+ "glob": "^13.0.6",
61
+ "globals": "^17.3.0",
62
+ "grunt": "^1.6.1",
63
+ "jest": "^30.2.0",
64
+ "liquid": "^5.1.1",
65
+ "nunjucks": "^3.2.4",
66
+ "pre-commit": "^1.2.2",
67
+ "prettier": "^3.8.1",
68
+ "twig": "^3.0.0",
69
+ "webpack": "^5.105.4"
68
70
  },
69
71
  "dependencies": {
70
- "html-minifier": "4.0.0"
72
+ "html-minifier-next": "^4.19.1"
71
73
  }
72
74
  }