aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2021-06-27 17:57:00 -0400
committerGitHub <noreply@github.com>2021-06-27 17:57:00 -0400
commit002da9fba8800b796ed98dd83e2ab68b4d37a1cc (patch)
treea500cec04f9a587926b2dc20290188a96b4bd209
parent24ef820ba816411288d8bc739f6e69abb511deb0 (diff)
Structured content style json (#1771)
* Install css * Remove unnecessary rule * Create CSS overrides file * Create script to generate CSS JSON file * Generate JSON * Add test
-rw-r--r--dev/data/structured-content-overrides.css50
-rw-r--r--dev/generate-structured-content-style.js191
-rw-r--r--ext/css/structured-content.css3
-rw-r--r--ext/data/structured-content-style.json330
-rw-r--r--package-lock.json33
-rw-r--r--package.json1
-rw-r--r--test/test-structured-content-style.js32
7 files changed, 637 insertions, 3 deletions
diff --git a/dev/data/structured-content-overrides.css b/dev/data/structured-content-overrides.css
new file mode 100644
index 00000000..31873760
--- /dev/null
+++ b/dev/data/structured-content-overrides.css
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 Yomichan Authors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the entrys of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+.gloss-image-container {
+ font-size: 1px;
+}
+.gloss-image-link[data-background=true]>.gloss-image-container {
+ /* remove-property background-color */
+}
+.gloss-image-container-overlay {
+ font-size: initial;
+ line-height: initial;
+ color: initial;
+}
+.gloss-image-background {
+ background-color: currentColor;
+}
+:root[data-browser=firefox] .gloss-image-link[data-image-rendering=crisp-edges] .gloss-image,
+:root[data-browser=firefox] .gloss-image-link[data-image-rendering=crisp-edges] .gloss-image-background,
+:root[data-browser=firefox-mobile] .gloss-image-link[data-image-rendering=crisp-edges] .gloss-image,
+:root[data-browser=firefox-mobile] .gloss-image-link[data-image-rendering=crisp-edges] .gloss-image-background {
+ /* remove-rule */
+}
+.gloss-image-link-text {
+ line-height: initial;
+}
+.gloss-sc-thead,
+.gloss-sc-tfoot,
+.gloss-sc-th {
+ /* remove-property background-color */
+}
+.gloss-sc-th,
+.gloss-sc-td {
+ border-width: 1px;
+ border-color: currentColor;
+}
diff --git a/dev/generate-structured-content-style.js b/dev/generate-structured-content-style.js
new file mode 100644
index 00000000..8335efc2
--- /dev/null
+++ b/dev/generate-structured-content-style.js
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2021 Yomichan Authors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+const fs = require('fs');
+const path = require('path');
+const css = require('css');
+const {testMain} = require('./util');
+
+function indexOfRule(rules, selectors) {
+ const jj = selectors.length;
+ for (let i = 0, ii = rules.length; i < ii; ++i) {
+ const ruleSelectors = rules[i].selectors;
+ if (ruleSelectors.length !== jj) { continue; }
+ let okay = true;
+ for (let j = 0; j < jj; ++j) {
+ if (selectors[j] !== ruleSelectors[j]) {
+ okay = false;
+ break;
+ }
+ }
+ if (okay) { return i; }
+ }
+ return -1;
+}
+
+function removeProperty(styles, property, removedProperties) {
+ let removeCount = removedProperties.get(property);
+ if (typeof removeCount !== 'undefined') { return removeCount; }
+ removeCount = 0;
+ for (let i = 0, ii = styles.length; i < ii; ++i) {
+ const key = styles[i][0];
+ if (key !== property) { continue; }
+ styles.splice(i, 1);
+ --i;
+ --ii;
+ ++removeCount;
+ }
+ removedProperties.set(property, removeCount);
+ return removeCount;
+}
+
+function formatRulesJson(rules) {
+ // Manually format JSON, for improved compactness
+ // return JSON.stringify(rules, null, 4);
+ const indent1 = ' ';
+ const indent2 = indent1.repeat(2);
+ const indent3 = indent1.repeat(3);
+ let result = '';
+ result += '[';
+ let index1 = 0;
+ for (const {selectors, styles} of rules) {
+ if (index1 > 0) { result += ','; }
+ result += `\n${indent1}{\n${indent2}"selectors": `;
+ if (selectors.length === 1) {
+ result += `[${JSON.stringify(selectors[0], null, 4)}]`;
+ } else {
+ result += JSON.stringify(selectors, null, 4).replace(/\n/g, '\n' + indent2);
+ }
+ result += `,\n${indent2}"styles": [`;
+ let index2 = 0;
+ for (const [key, value] of styles) {
+ if (index2 > 0) { result += ','; }
+ result += `\n${indent3}[${JSON.stringify(key)}, ${JSON.stringify(value)}]`;
+ ++index2;
+ }
+ if (index2 > 0) { result += `\n${indent2}`; }
+ result += `]\n${indent1}}`;
+ ++index1;
+ }
+ if (index1 > 0) { result += '\n'; }
+ result += ']';
+ return result;
+}
+
+function generateRules() {
+ const content1 = fs.readFileSync(path.join(__dirname, '..', 'ext/css/structured-content.css'), {encoding: 'utf8'});
+ const content2 = fs.readFileSync(path.join(__dirname, 'data/structured-content-overrides.css'), {encoding: 'utf8'});
+ const stylesheet1 = css.parse(content1, {}).stylesheet;
+ const stylesheet2 = css.parse(content2, {}).stylesheet;
+
+ const removePropertyPattern = /^remove-property\s+([a-zA-Z0-9-]+)$/;
+ const removeRulePattern = /^remove-rule$/;
+
+ const rules = [];
+
+ // Default stylesheet
+ for (const rule of stylesheet1.rules) {
+ if (rule.type !== 'rule') { continue; }
+ const {selectors, declarations} = rule;
+ const styles = [];
+ for (const declaration of declarations) {
+ if (declaration.type !== 'declaration') { console.log(declaration); continue; }
+ const {property, value} = declaration;
+ styles.push([property, value]);
+ }
+ if (styles.length > 0) {
+ rules.push({selectors, styles});
+ }
+ }
+
+ // Overrides
+ for (const rule of stylesheet2.rules) {
+ if (rule.type !== 'rule') { continue; }
+ const {selectors, declarations} = rule;
+ const removedProperties = new Map();
+ for (const declaration of declarations) {
+ switch (declaration.type) {
+ case 'declaration':
+ {
+ const index = indexOfRule(rules, selectors);
+ let entry;
+ if (index >= 0) {
+ entry = rules[index];
+ } else {
+ entry = {selectors, styles: []};
+ rules.push(entry);
+ }
+ const {property, value} = declaration;
+ removeProperty(entry.styles, property, removedProperties);
+ entry.styles.push([property, value]);
+ }
+ break;
+ case 'comment':
+ {
+ const index = indexOfRule(rules, selectors);
+ if (index < 0) { throw new Error('Could not find rule with matching selectors'); }
+ const comment = declaration.comment.trim();
+ let m;
+ if ((m = removePropertyPattern.exec(comment)) !== null) {
+ const property = m[1];
+ const removeCount = removeProperty(rules[index].styles, property, removedProperties);
+ if (removeCount === 0) { throw new Error(`Property removal is unnecessary; ${property} does not exist`); }
+ } else if ((m = removeRulePattern.exec(comment)) !== null) {
+ rules.splice(index, 1);
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ // Remove empty
+ for (let i = 0, ii = rules.length; i < ii; ++i) {
+ if (rules[i].styles.length > 0) { continue; }
+ rules.splice(i, 1);
+ --i;
+ --ii;
+ }
+
+ return rules;
+}
+
+function generateRulesJson() {
+ return formatRulesJson(generateRules());
+}
+
+function getOutputPath() {
+ return path.join(__dirname, '..', 'ext/data/structured-content-style.json');
+}
+
+function main() {
+ const outputFileName = getOutputPath();
+ const json = generateRulesJson();
+ fs.writeFileSync(outputFileName, json, {encoding: 'utf8'});
+}
+
+
+if (require.main === module) {
+ testMain(main, process.argv.slice(2));
+}
+
+
+module.exports = {
+ generateRules,
+ generateRulesJson,
+ getOutputPath
+};
diff --git a/ext/css/structured-content.css b/ext/css/structured-content.css
index cff2c83c..ea0bf5cd 100644
--- a/ext/css/structured-content.css
+++ b/ext/css/structured-content.css
@@ -201,9 +201,6 @@
table-layout: auto;
border-collapse: collapse;
}
-.gloss-sc-tbody {
- background-color: transparent;
-}
.gloss-sc-thead,
.gloss-sc-tfoot,
.gloss-sc-th {
diff --git a/ext/data/structured-content-style.json b/ext/data/structured-content-style.json
new file mode 100644
index 00000000..80a71e1c
--- /dev/null
+++ b/ext/data/structured-content-style.json
@@ -0,0 +1,330 @@
+[
+ {
+ "selectors": [".gloss-image-container"],
+ "styles": [
+ ["display", "inline-block"],
+ ["white-space", "nowrap"],
+ ["max-width", "100%"],
+ ["position", "relative"],
+ ["vertical-align", "top"],
+ ["line-height", "0"],
+ ["overflow", "hidden"],
+ ["font-size", "1px"]
+ ]
+ },
+ {
+ "selectors": [".gloss-image-link"],
+ "styles": [
+ ["cursor", "inherit"],
+ ["color", "inherit"],
+ ["display", "inline-block"],
+ ["position", "relative"],
+ ["line-height", "1"]
+ ]
+ },
+ {
+ "selectors": [".gloss-image-link[href]:hover"],
+ "styles": [
+ ["cursor", "pointer"]
+ ]
+ },
+ {
+ "selectors": [".gloss-image-container-overlay"],
+ "styles": [
+ ["position", "absolute"],
+ ["left", "0"],
+ ["top", "0"],
+ ["width", "100%"],
+ ["height", "100%"],
+ ["display", "table"],
+ ["table-layout", "fixed"],
+ ["white-space", "normal"],
+ ["font-size", "initial"],
+ ["line-height", "initial"],
+ ["color", "initial"]
+ ]
+ },
+ {
+ "selectors": [".gloss-image-link[data-has-image=true][data-image-load-state=load-error] .gloss-image-container-overlay::after"],
+ "styles": [
+ ["content", "'Image failed to load'"],
+ ["display", "table-cell"],
+ ["width", "100%"],
+ ["height", "100%"],
+ ["vertical-align", "middle"],
+ ["text-align", "center"],
+ ["padding", "0.25em"]
+ ]
+ },
+ {
+ "selectors": [".gloss-image-background"],
+ "styles": [
+ ["--image", "none"],
+ ["position", "absolute"],
+ ["left", "0"],
+ ["top", "0"],
+ ["width", "100%"],
+ ["height", "100%"],
+ ["-webkit-mask-repeat", "no-repeat"],
+ ["-webkit-mask-position", "center center"],
+ ["-webkit-mask-mode", "alpha"],
+ ["-webkit-mask-size", "contain"],
+ ["-webkit-mask-image", "var(--image)"],
+ ["mask-repeat", "no-repeat"],
+ ["mask-position", "center center"],
+ ["mask-mode", "alpha"],
+ ["mask-size", "contain"],
+ ["mask-image", "var(--image)"],
+ ["background-color", "currentColor"]
+ ]
+ },
+ {
+ "selectors": [".gloss-image"],
+ "styles": [
+ ["display", "inline-block"],
+ ["vertical-align", "top"],
+ ["object-fit", "contain"],
+ ["border", "none"],
+ ["outline", "none"]
+ ]
+ },
+ {
+ "selectors": [".gloss-image-link[data-has-aspect-ratio=true] .gloss-image"],
+ "styles": [
+ ["position", "absolute"],
+ ["left", "0"],
+ ["top", "0"],
+ ["width", "100%"],
+ ["height", "100%"]
+ ]
+ },
+ {
+ "selectors": [".gloss-image:not([src])"],
+ "styles": [
+ ["display", "none"]
+ ]
+ },
+ {
+ "selectors": [
+ ".gloss-image-link[data-image-rendering=pixelated] .gloss-image",
+ ".gloss-image-link[data-image-rendering=pixelated] .gloss-image-background"
+ ],
+ "styles": [
+ ["image-rendering", "auto"],
+ ["image-rendering", "-moz-crisp-edges"],
+ ["image-rendering", "-webkit-optimize-contrast"],
+ ["image-rendering", "pixelated"],
+ ["image-rendering", "crisp-edges"]
+ ]
+ },
+ {
+ "selectors": [
+ ".gloss-image-link[data-image-rendering=crisp-edges] .gloss-image",
+ ".gloss-image-link[data-image-rendering=crisp-edges] .gloss-image-background"
+ ],
+ "styles": [
+ ["image-rendering", "auto"],
+ ["image-rendering", "-moz-crisp-edges"],
+ ["image-rendering", "-webkit-optimize-contrast"],
+ ["image-rendering", "crisp-edges"]
+ ]
+ },
+ {
+ "selectors": [".gloss-image-link[data-has-aspect-ratio=true] .gloss-image-aspect-ratio-sizer"],
+ "styles": [
+ ["display", "inline-block"],
+ ["width", "0"],
+ ["vertical-align", "top"],
+ ["font-size", "0"]
+ ]
+ },
+ {
+ "selectors": [".gloss-image-link-text"],
+ "styles": [
+ ["display", "none"],
+ ["line-height", "initial"]
+ ]
+ },
+ {
+ "selectors": [".gloss-image-link-text::before"],
+ "styles": [
+ ["content", "'['"]
+ ]
+ },
+ {
+ "selectors": [".gloss-image-link-text::after"],
+ "styles": [
+ ["content", "']'"]
+ ]
+ },
+ {
+ "selectors": [".gloss-image-description"],
+ "styles": [
+ ["display", "block"],
+ ["white-space", "pre-line"]
+ ]
+ },
+ {
+ "selectors": [".gloss-image-link[data-appearance=monochrome] .gloss-image"],
+ "styles": [
+ ["visibility", "hidden"]
+ ]
+ },
+ {
+ "selectors": [".gloss-image-link:not([data-appearance=monochrome]) .gloss-image-background"],
+ "styles": [
+ ["display", "none"]
+ ]
+ },
+ {
+ "selectors": [".gloss-image-link[data-size-units=em] .gloss-image-container"],
+ "styles": [
+ ["font-size", "1em"]
+ ]
+ },
+ {
+ "selectors": [".gloss-image-link[data-vertical-align=baseline]"],
+ "styles": [
+ ["vertical-align", "baseline"]
+ ]
+ },
+ {
+ "selectors": [".gloss-image-link[data-vertical-align=sub]"],
+ "styles": [
+ ["vertical-align", "sub"]
+ ]
+ },
+ {
+ "selectors": [".gloss-image-link[data-vertical-align=super]"],
+ "styles": [
+ ["vertical-align", "super"]
+ ]
+ },
+ {
+ "selectors": [".gloss-image-link[data-vertical-align=text-top]"],
+ "styles": [
+ ["vertical-align", "top"]
+ ]
+ },
+ {
+ "selectors": [".gloss-image-link[data-vertical-align=text-bottom]"],
+ "styles": [
+ ["vertical-align", "bottom"]
+ ]
+ },
+ {
+ "selectors": [".gloss-image-link[data-vertical-align=middle]"],
+ "styles": [
+ ["vertical-align", "middle"]
+ ]
+ },
+ {
+ "selectors": [".gloss-image-link[data-vertical-align=top]"],
+ "styles": [
+ ["vertical-align", "top"]
+ ]
+ },
+ {
+ "selectors": [".gloss-image-link[data-vertical-align=bottom]"],
+ "styles": [
+ ["vertical-align", "bottom"]
+ ]
+ },
+ {
+ "selectors": [
+ ".gloss-image-link[data-collapsed=true]",
+ ":root[data-glossary-layout-mode=compact] .gloss-image-link[data-collapsible=true]"
+ ],
+ "styles": [
+ ["vertical-align", "baseline"]
+ ]
+ },
+ {
+ "selectors": [
+ ".gloss-image-link[data-collapsed=true] .gloss-image-container",
+ ":root[data-glossary-layout-mode=compact] .gloss-image-link[data-collapsible=true] .gloss-image-container"
+ ],
+ "styles": [
+ ["display", "none"],
+ ["position", "absolute"],
+ ["left", "0"],
+ ["top", "100%"],
+ ["z-index", "1"]
+ ]
+ },
+ {
+ "selectors": [
+ ".entry:nth-last-of-type(1):not(:nth-of-type(1)) .gloss-image-link[data-collapsed=true] .gloss-image-container",
+ ":root[data-glossary-layout-mode=compact] .entry:nth-last-of-type(1):not(:nth-of-type(1)) .gloss-image-link[data-collapsible=true] .gloss-image-container"
+ ],
+ "styles": [
+ ["bottom", "100%"],
+ ["top", "auto"]
+ ]
+ },
+ {
+ "selectors": [
+ ".gloss-image-link[data-collapsed=true]:hover .gloss-image-container",
+ ".gloss-image-link[data-collapsed=true]:focus .gloss-image-container",
+ ":root[data-glossary-layout-mode=compact] .gloss-image-link[data-collapsible=true]:hover .gloss-image-container",
+ ":root[data-glossary-layout-mode=compact] .gloss-image-link[data-collapsible=true]:focus .gloss-image-container"
+ ],
+ "styles": [
+ ["display", "block"]
+ ]
+ },
+ {
+ "selectors": [
+ ".gloss-image-link[data-collapsed=true] .gloss-image-link-text",
+ ":root[data-glossary-layout-mode=compact] .gloss-image-link[data-collapsible=true] .gloss-image-link-text"
+ ],
+ "styles": [
+ ["display", "inline"]
+ ]
+ },
+ {
+ "selectors": [
+ ".gloss-image-link[data-collapsed=true]~.gloss-image-description",
+ ":root[data-glossary-layout-mode=compact] .gloss-image-description"
+ ],
+ "styles": [
+ ["display", "inline"]
+ ]
+ },
+ {
+ "selectors": [".gloss-sc-table-container"],
+ "styles": [
+ ["display", "block"]
+ ]
+ },
+ {
+ "selectors": [".gloss-sc-table"],
+ "styles": [
+ ["table-layout", "auto"],
+ ["border-collapse", "collapse"]
+ ]
+ },
+ {
+ "selectors": [
+ ".gloss-sc-thead",
+ ".gloss-sc-tfoot",
+ ".gloss-sc-th"
+ ],
+ "styles": [
+ ["font-weight", "bold"]
+ ]
+ },
+ {
+ "selectors": [
+ ".gloss-sc-th",
+ ".gloss-sc-td"
+ ],
+ "styles": [
+ ["border-style", "solid"],
+ ["padding", "0.25em"],
+ ["vertical-align", "top"],
+ ["border-width", "1px"],
+ ["border-color", "currentColor"]
+ ]
+ }
+] \ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index bb1336e3..956b433e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1023,6 +1023,12 @@
"integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
"dev": true
},
+ "atob": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+ "dev": true
+ },
"atomic-sleep": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
@@ -2067,6 +2073,17 @@
"integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==",
"dev": true
},
+ "css": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz",
+ "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.4",
+ "source-map": "^0.6.1",
+ "source-map-resolve": "^0.6.0"
+ }
+ },
"css-select": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.3.tgz",
@@ -2231,6 +2248,12 @@
"integrity": "sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw==",
"dev": true
},
+ "decode-uri-component": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
+ "dev": true
+ },
"decompress-response": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
@@ -6606,6 +6629,16 @@
"integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==",
"dev": true
},
+ "source-map-resolve": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz",
+ "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==",
+ "dev": true,
+ "requires": {
+ "atob": "^2.1.2",
+ "decode-uri-component": "^0.2.0"
+ }
+ },
"source-map-support": {
"version": "0.5.19",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
diff --git a/package.json b/package.json
index ddc2648a..872f7dbb 100644
--- a/package.json
+++ b/package.json
@@ -36,6 +36,7 @@
},
"devDependencies": {
"browserify": "^17.0.0",
+ "css": "^3.0.0",
"eslint": "^7.29.0",
"eslint-plugin-header": "^3.1.1",
"eslint-plugin-no-unsanitized": "^3.1.5",
diff --git a/test/test-structured-content-style.js b/test/test-structured-content-style.js
new file mode 100644
index 00000000..ed8c24e9
--- /dev/null
+++ b/test/test-structured-content-style.js
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 Yomichan Authors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+const fs = require('fs');
+const assert = require('assert');
+const {testMain} = require('../dev/util');
+const {generateRulesJson, getOutputPath} = require('../dev/generate-structured-content-style');
+
+
+function main() {
+ const outputPath = getOutputPath();
+ const actual = fs.readFileSync(outputPath, {encoding: 'utf8'});
+ const expected = generateRulesJson();
+ assert.deepStrictEqual(actual, expected);
+}
+
+
+if (require.main === module) { testMain(main); }