Skip to content

Commit 922e47f

Browse files
committed
Wire up all the way to making migration folder, but with bogus css migration
1 parent 39d5336 commit 922e47f

File tree

3 files changed

+167
-72
lines changed

3 files changed

+167
-72
lines changed

custom-css-analysis-and-migration/create-migrations.ts

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,76 @@
33
// See README.md for use.
44

55
import fs from "fs";
6+
import crypto from "crypto";
7+
import { migrateCssToAppearance } from "./migrateCssToAppearance";
68

79
const data = fs.readFileSync("./output/filter-output.json", "utf8");
10+
const records = JSON.parse(data);
811

9-
// TODO: see https://issues.bloomlibrary.org/youtrack/issue/BL-12857
12+
/* a typical rule looks like this:
13+
{
14+
"book_count": 1,
15+
"unique_named_books": 1,
16+
"first_book": "https://bloomlibrary.org/:search:bookInstanceId%3Arajib_mitra@sil-lead.org",
17+
"css": ".marginBox{left:3mm}",
18+
"paths": [
19+
"output/downloads/[email protected]/019ec209-8918-4d0b-b4ac-1ce3f146e256/Koni ni Ɲɔhɔɲɛgɛnɛ Jojo"
20+
],
21+
"uniqueified_paths": [
22+
"output/downloads/[email protected]/019ec209-8918-4d0b-b4ac-1ce3f146e256/Koni ni Ɲɔhɔɲɛgɛnɛ Jojo"
23+
]
24+
}
25+
*/
26+
27+
// Delete all files and folders in the output folder
28+
fs.rmdirSync("./output/migration", { recursive: true });
29+
fs.mkdirSync("./output/migration");
30+
31+
// For each record, get a checksum of the css, and use that as the name of the folder.
32+
// Then create a folder with that name, and copy the css into it.
33+
34+
let count = 0;
35+
for (const record of records) {
36+
if (record.css === undefined) continue;
37+
38+
++count;
39+
if (Bun.argv.length > 2 && count >= Number.parseInt(Bun.argv[2])) break;
40+
console.log(`css = ${record.css}`);
41+
const checksum = crypto.createHash("md5").update(record.css).digest("hex");
42+
const folder = `./output/migration/${count} ${checksum}`;
43+
fs.mkdirSync(folder);
44+
45+
fs.writeFileSync(
46+
`${folder}/customBookStyles.css`,
47+
migrateCssToAppearance(record.css) +
48+
`\n\n /* ----- ORIGINAL ---- */\n/*${record.css.replaceAll(
49+
"*/",
50+
"#/"
51+
)}*/`
52+
);
53+
54+
const uniqueUploaders = [
55+
...new Set(
56+
record.paths.map((path: string) => {
57+
const email = path.split("/")[2];
58+
const emailParts = email.split("@");
59+
const obfuscatedEmail =
60+
emailParts[0].slice(0, -2) + "..." + "@" + emailParts[1];
61+
return obfuscatedEmail;
62+
})
63+
),
64+
].slice(0, 3); // first 3 are enough to give a sense of who uploaded these books
65+
66+
fs.writeFileSync(
67+
`${folder}/appearance.json`,
68+
`// Matches customBookStyles.css with checksum ${checksum}
69+
// This was used by ${record.book_count} books (${record.unique_named_books} unique).` +
70+
// todo: +`// Affected branding projects include "Mali-ACR-2020-Soninke".`
71+
`// Uploaders included ${JSON.stringify(uniqueUploaders)}.
72+
// Example Book: ${record.first_book}
73+
// A replacement customBookStyles.css has been generated.
74+
{
75+
}
76+
`
77+
);
78+
}

custom-css-analysis-and-migration/filter-stylesheets.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,13 @@ console.write(`total unique css files: ${records.length}\r\n`);
1818
const kProbablyWillInterfere = `\\.marginBox\\s*\\{[^\\}]*?(?<![-\\w])(padding-|left:|top:|right:|bottom:|margin-|width:|height:)[^\\}]*\\}`;
1919
const kProbablyWillInterfereRegex = new RegExp(kProbablyWillInterfere, "gi");
2020

21-
const filteredRecords = records.filter((record) => {
22-
return kProbablyWillInterfereRegex.test(record.content);
23-
});
21+
const max = Bun.argv.length > 2 ? Number.parseInt(Bun.argv[2]) : 10000000;
22+
23+
const filteredRecords = records
24+
.filter((record) => {
25+
return kProbablyWillInterfereRegex.test(record.content);
26+
})
27+
.slice(0, max);
2428

2529
// in filteredRecords.paths, we want to remove any path that has the same filename as another path
2630
const recordsWithUniqueifiedPaths = filteredRecords.map((record) => {

custom-css-analysis-and-migration/migrate-css-files-to-variable-system.ts renamed to custom-css-analysis-and-migration/migrateCssToAppearance.ts

Lines changed: 90 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -75,90 +75,103 @@ const coverPropertyConversions: PropertyConversions = {
7575
width: "--page-margin-right",
7676
};
7777

78-
const cssFileContent: string = fs.readFileSync("./zebra.css", "utf8");
79-
const cssObject: Stylesheet = parse(cssFileContent);
80-
81-
if (cssObject.stylesheet && cssObject.stylesheet.rules) {
82-
cssObject.stylesheet.rules.forEach((rule: Rule) => {
83-
if (
84-
rule.type === "rule" &&
85-
rule.selectors &&
86-
rule.selectors.some((s: string) => s.includes(".marginBox"))
87-
) {
88-
// remove the .marginBox class from the selectors
89-
rule.selectors = rule.selectors.map((s: string) =>
90-
s.replace(".marginBox", "")
91-
);
92-
93-
// find the sizes element that has the same name as one of the selectors
94-
const size = sizes.find((sz) =>
95-
rule.selectors!.some((sel) => sel.includes(sz.name))
96-
);
97-
98-
var x = rule.declarations?.find(
99-
(d: Declaration) => d.property === "left"
100-
) as Declaration;
101-
102-
var left = Number.parseFloat(
103-
(
78+
export function migrateCssToAppearance(cssFileContent: string): string {
79+
var cssObject: Stylesheet;
80+
try {
81+
cssObject = parse(cssFileContent);
82+
} catch (e) {
83+
console.log("Error parsing css: " + e);
84+
return cssFileContent;
85+
}
86+
87+
if (cssObject.stylesheet && cssObject.stylesheet.rules) {
88+
cssObject.stylesheet.rules.forEach((rule: Rule) => {
89+
if (
90+
rule.type === "rule" &&
91+
rule.selectors &&
92+
rule.selectors.some((s: string) => s.includes(".marginBox"))
93+
) {
94+
// THE Following removal, LIKE A LOT OF THIS FILE, IS GARBAGE. A RULE LIKE
95+
// .marginBox { background-color: red; } is just fine.
96+
97+
// remove the .marginBox class from the selectors
98+
rule.selectors = rule.selectors.map((s: string) =>
99+
s.replace(".marginBox", "")
100+
);
101+
102+
// find the sizes element that has the same name as one of the selectors
103+
const size = sizes.find((sz) =>
104+
rule.selectors!.some((sel) => sel.includes(sz.name))
105+
);
106+
107+
var x = rule.declarations?.find(
108+
(d: Declaration) => d.property === "left"
109+
) as Declaration;
110+
111+
const l = (
104112
rule.declarations?.find(
105113
(d: Declaration) => d.property === "left"
106114
) as Declaration
107-
).value!
108-
);
109-
var top = Number.parseFloat(
110-
(
115+
)?.value!;
116+
117+
const t = (
111118
rule.declarations?.find(
112119
(d: Declaration) => d.property === "top"
113120
) as Declaration
114-
).value!
115-
);
116-
rule.declarations?.forEach((declaration: Declaration) => {
117-
if (declaration.type === "declaration") {
118-
const key = (declaration as Declaration).property;
119-
120-
if (size) {
121-
if (key === "width") {
122-
declaration.value =
123-
size.width -
124-
Number.parseFloat(declaration.value!) -
125-
left +
126-
"mm";
121+
)?.value!;
122+
123+
if (!l || !t) return; // todo log it?
124+
125+
var left = parseFloatOrUndefined(l);
126+
var top = parseFloatOrUndefined(t);
127+
rule.declarations?.forEach((declaration: Declaration) => {
128+
if (declaration.type === "declaration") {
129+
const key = (declaration as Declaration).property;
130+
131+
if (size) {
132+
const v = parseFloatOrUndefined(declaration.value!);
133+
if (v === undefined || left === undefined || top === undefined)
134+
declaration.value = ` ignore /* error: ${rule.declarations?.toString()} */`;
135+
else {
136+
if (key === "width") {
137+
declaration.value = size.width - v - left + "mm";
138+
}
139+
if (key === "height") {
140+
declaration.value = size.height - v - top + "mm";
141+
}
142+
}
127143
}
128-
if (key === "height") {
129-
declaration.value =
130-
size.height -
131-
Number.parseFloat(declaration.value!) -
132-
top +
133-
"mm";
144+
145+
const isCover = rule.selectors!.some((sel) =>
146+
sel.includes("Cover")
147+
);
148+
const map = isCover
149+
? coverPropertyConversions
150+
: propertyConversions;
151+
if (declaration.property! in map) {
152+
declaration.property = map[declaration.property!];
153+
declaration.value = declaration.value?.replace("!important", "");
134154
}
135155
}
156+
});
136157

137-
const isCover = rule.selectors!.some((sel) => sel.includes("Cover"));
138-
const map = isCover ? coverPropertyConversions : propertyConversions;
139-
if (declaration.property! in map) {
140-
declaration.property = map[declaration.property!];
141-
declaration.value = declaration.value?.replace("!important", "");
142-
}
143-
}
144-
});
158+
// danger, this would probably break if there is anything but classes in the selector
159+
sortClassesInSelector(rule);
145160

146-
// danger, this would probably break if there is anything but classes in the selector
147-
sortClassesInSelector(rule);
161+
// TODO: this doesn't yet move top and bottom margins with .outsideFrontCover and .outsideBackCover to --cover-margin-top and --cover-margin-bottom
148162

149-
// TODO: this doesn't yet move top and bottom margins with .outsideFrontCover and .outsideBackCover to --cover-margin-top and --cover-margin-bottom
163+
sortDeclarations(rule);
164+
}
165+
});
150166

151-
sortDeclarations(rule);
152-
}
153-
});
167+
// danger, normally sorting rules is not a good idea!
168+
cssObject.stylesheet.rules = sortRules(cssObject.stylesheet.rules);
169+
}
154170

155-
// danger, normally sorting rules is not a good idea!
156-
cssObject.stylesheet.rules = sortRules(cssObject.stylesheet.rules);
171+
const modifiedCss: string = stringify(cssObject);
172+
return modifiedCss;
157173
}
158174

159-
const modifiedCss: string = stringify(cssObject);
160-
console.log(modifiedCss);
161-
162175
function sortDeclarations(rule: Rule) {
163176
// Define the type of propertyConversions
164177
type PropertyConversions = {
@@ -215,3 +228,12 @@ function sortRules(rules: Rule[]): Rule[] {
215228
}
216229
});
217230
}
231+
232+
function parseFloatOrUndefined(value: string): number | undefined {
233+
try {
234+
const result = parseFloat(value);
235+
return isNaN(result) ? undefined : result;
236+
} catch (e) {
237+
return undefined;
238+
}
239+
}

0 commit comments

Comments
 (0)