@gem-sdk/hash-class-names 1.0.0

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.
@@ -0,0 +1,111 @@
1
+ 'use strict';
2
+
3
+ var parse5 = require('parse5');
4
+
5
+ const hashClassNames = (html, css, options) => {
6
+ const documentFragment = parse5.parseFragment(html);
7
+ const jsonHTML = documentFragment.childNodes;
8
+ if (jsonHTML?.length) {
9
+ const classes = {};
10
+ const hashClasses = {};
11
+ // Find & Obfuscate class in html
12
+ loopNode(jsonHTML, (node) => {
13
+ if (node?.attrs?.length) {
14
+ const attrClass = node.attrs.find((item) => item.name == 'class');
15
+ if (attrClass?.value) {
16
+ const nodeClasses = attrClass.value.split(' ');
17
+ if (nodeClasses?.length) {
18
+ for (let i = 0; i < nodeClasses.length; i++) {
19
+ const nodeClass = nodeClasses[i]?.trim() || '';
20
+ if (options?.ignoreClasses?.includes(nodeClass)) {
21
+ continue;
22
+ }
23
+ if (nodeClass) {
24
+ const data = classes[nodeClass];
25
+ if (!data) {
26
+ // eslint-disable-next-line no-constant-condition
27
+ while (true) {
28
+ const newClass = `a${ID()}`;
29
+ if (!hashClasses[newClass]) {
30
+ hashClasses[newClass] = true; // flag
31
+ // Cache
32
+ classes[nodeClass] = {
33
+ hash: newClass,
34
+ };
35
+ // replace
36
+ attrClass.value = attrClass.value.replace(nodeClass, newClass);
37
+ break;
38
+ }
39
+ }
40
+ }
41
+ else {
42
+ attrClass.value = attrClass.value.replace(nodeClass, data.hash);
43
+ }
44
+ }
45
+ }
46
+ }
47
+ }
48
+ }
49
+ });
50
+ let newHTML = '';
51
+ for (let i = 0; i < jsonHTML.length; i++) {
52
+ const node = jsonHTML[i];
53
+ if (node) {
54
+ newHTML += parse5.serializeOuter(node);
55
+ }
56
+ }
57
+ // Replace class in css
58
+ const orderClasses = [];
59
+ for (const oldClass in classes) {
60
+ if (Object.prototype.hasOwnProperty.call(classes, oldClass)) {
61
+ const data = classes[oldClass];
62
+ if (data?.hash) {
63
+ orderClasses.push({
64
+ oldClass: oldClass,
65
+ newClass: data.hash,
66
+ });
67
+ }
68
+ }
69
+ }
70
+ orderClasses.sort((a, b) => {
71
+ if (a.oldClass.includes(b.oldClass)) {
72
+ return -1;
73
+ }
74
+ return 1;
75
+ });
76
+ let newCSS = css;
77
+ for (let i = 0; i < orderClasses.length; i++) {
78
+ const data = orderClasses[i];
79
+ newCSS = newCSS.replaceAll(`.${data?.oldClass}`, `.${data?.newClass}`);
80
+ }
81
+ return {
82
+ html: newHTML,
83
+ css: newCSS,
84
+ };
85
+ }
86
+ return {
87
+ html,
88
+ css,
89
+ };
90
+ };
91
+ const loopNode = (childNodes, callback) => {
92
+ if (childNodes?.length) {
93
+ for (let i = 0; i < childNodes.length; i++) {
94
+ const childNode = childNodes[i];
95
+ if (childNode) {
96
+ callback(childNode);
97
+ if (childNode.childNodes?.length) {
98
+ loopNode(childNode.childNodes, callback);
99
+ }
100
+ }
101
+ }
102
+ }
103
+ };
104
+ const ID = function () {
105
+ // Math.random should be unique because of its seeding algorithm.
106
+ // Convert it to base 36 (numbers + letters), and grab the first 9 characters
107
+ // after the decimal.
108
+ return Math.random().toString(36).substr(2, 5);
109
+ };
110
+
111
+ exports.hashClassNames = hashClassNames;
@@ -0,0 +1,109 @@
1
+ import { parseFragment, serializeOuter } from 'parse5';
2
+
3
+ const hashClassNames = (html, css, options) => {
4
+ const documentFragment = parseFragment(html);
5
+ const jsonHTML = documentFragment.childNodes;
6
+ if (jsonHTML?.length) {
7
+ const classes = {};
8
+ const hashClasses = {};
9
+ // Find & Obfuscate class in html
10
+ loopNode(jsonHTML, (node) => {
11
+ if (node?.attrs?.length) {
12
+ const attrClass = node.attrs.find((item) => item.name == 'class');
13
+ if (attrClass?.value) {
14
+ const nodeClasses = attrClass.value.split(' ');
15
+ if (nodeClasses?.length) {
16
+ for (let i = 0; i < nodeClasses.length; i++) {
17
+ const nodeClass = nodeClasses[i]?.trim() || '';
18
+ if (options?.ignoreClasses?.includes(nodeClass)) {
19
+ continue;
20
+ }
21
+ if (nodeClass) {
22
+ const data = classes[nodeClass];
23
+ if (!data) {
24
+ // eslint-disable-next-line no-constant-condition
25
+ while (true) {
26
+ const newClass = `a${ID()}`;
27
+ if (!hashClasses[newClass]) {
28
+ hashClasses[newClass] = true; // flag
29
+ // Cache
30
+ classes[nodeClass] = {
31
+ hash: newClass,
32
+ };
33
+ // replace
34
+ attrClass.value = attrClass.value.replace(nodeClass, newClass);
35
+ break;
36
+ }
37
+ }
38
+ }
39
+ else {
40
+ attrClass.value = attrClass.value.replace(nodeClass, data.hash);
41
+ }
42
+ }
43
+ }
44
+ }
45
+ }
46
+ }
47
+ });
48
+ let newHTML = '';
49
+ for (let i = 0; i < jsonHTML.length; i++) {
50
+ const node = jsonHTML[i];
51
+ if (node) {
52
+ newHTML += serializeOuter(node);
53
+ }
54
+ }
55
+ // Replace class in css
56
+ const orderClasses = [];
57
+ for (const oldClass in classes) {
58
+ if (Object.prototype.hasOwnProperty.call(classes, oldClass)) {
59
+ const data = classes[oldClass];
60
+ if (data?.hash) {
61
+ orderClasses.push({
62
+ oldClass: oldClass,
63
+ newClass: data.hash,
64
+ });
65
+ }
66
+ }
67
+ }
68
+ orderClasses.sort((a, b) => {
69
+ if (a.oldClass.includes(b.oldClass)) {
70
+ return -1;
71
+ }
72
+ return 1;
73
+ });
74
+ let newCSS = css;
75
+ for (let i = 0; i < orderClasses.length; i++) {
76
+ const data = orderClasses[i];
77
+ newCSS = newCSS.replaceAll(`.${data?.oldClass}`, `.${data?.newClass}`);
78
+ }
79
+ return {
80
+ html: newHTML,
81
+ css: newCSS,
82
+ };
83
+ }
84
+ return {
85
+ html,
86
+ css,
87
+ };
88
+ };
89
+ const loopNode = (childNodes, callback) => {
90
+ if (childNodes?.length) {
91
+ for (let i = 0; i < childNodes.length; i++) {
92
+ const childNode = childNodes[i];
93
+ if (childNode) {
94
+ callback(childNode);
95
+ if (childNode.childNodes?.length) {
96
+ loopNode(childNode.childNodes, callback);
97
+ }
98
+ }
99
+ }
100
+ }
101
+ };
102
+ const ID = function () {
103
+ // Math.random should be unique because of its seeding algorithm.
104
+ // Convert it to base 36 (numbers + letters), and grab the first 9 characters
105
+ // after the decimal.
106
+ return Math.random().toString(36).substr(2, 5);
107
+ };
108
+
109
+ export { hashClassNames };
@@ -0,0 +1,15 @@
1
+ type Node = {
2
+ attrs?: {
3
+ name: string;
4
+ value: string;
5
+ }[];
6
+ childNodes?: Node[];
7
+ };
8
+ declare const hashClassNames: (html: string, css: string, options?: {
9
+ ignoreClasses?: string[];
10
+ }) => {
11
+ html: string;
12
+ css: string;
13
+ };
14
+
15
+ export { Node, hashClassNames };
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@gem-sdk/hash-class-names",
3
+ "version": "1.0.0",
4
+ "license": "MIT",
5
+ "main": "dist/cjs/index.js",
6
+ "scripts": {
7
+ "cleanup": "rimraf es dist lib",
8
+ "prebuild": "yarn cleanup",
9
+ "pre:publish": "node ./../../helpers/convert-publish.js -p",
10
+ "post:publish": "node ./../../helpers/convert-publish.js",
11
+ "watch": "rollup -c ./../../helpers/rollup.config.mjs -w",
12
+ "build": "rollup -c ./../../helpers/rollup.config.mjs --environment NODE_ENV:production",
13
+ "lint": "eslint ./src --ext .tsx,.ts",
14
+ "type-check": "yarn tsc --noEmit",
15
+ "test": "jest -c ./../../helpers/jest.config.ts"
16
+ },
17
+ "dependencies": {
18
+ "parse5": "^7.1.2"
19
+ },
20
+ "module": "dist/esm/index.js",
21
+ "types": "dist/types/index.d.ts",
22
+ "exports": {
23
+ "./package.json": "./package.json",
24
+ ".": {
25
+ "import": "./dist/esm/index.js",
26
+ "require": "./dist/cjs/index.js",
27
+ "types": "./dist/types/index.d.ts"
28
+ }
29
+ },
30
+ "files": [
31
+ "dist"
32
+ ]
33
+ }