liudong
2023-05-29 340f156319b863525e50e900c58e59b86ecb3d5e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#!/usr/bin/env node
 
// This script can be used to build custom colour-scheme css files.
//
// 1. Create a copy of packages/node_modules/@node-red/editor-client/src/sass/colors.scss
//    and change the values to the desired colours.
//
// 2. Run this script, providing the path to the custom file using the --in option
//
// 3. Save the output of the script to a file - either redirect its output,
//    or use the --out option.
//
// 4. Edit your settings file to set the theme:
//       editorTheme: {
//           page: {
//               css: "/path/to/file/generated/by/this/script"
//           }
//       }
//
// 5. Restart Node-RED
//
 
 
 
const os = require("os");
const nopt = require("nopt");
const path = require("path");
const fs = require("fs-extra");
const sass = require("sass");
 
const knownOpts = {
    "help": Boolean,
    "long": Boolean,
    "in": [path],
    "out": [path]
};
const shortHands = {
    "?":["--help"]
};
nopt.invalidHandler = function(k,v,t) {}
 
const parsedArgs = nopt(knownOpts,shortHands,process.argv,2)
 
if (parsedArgs.help) {
    console.log("Usage: build-custom-theme [-?] [--in FILE] [--out FILE]");
    console.log("");
    console.log("Options:");
    console.log("  --in  FILE  Custom colors sass file");
    console.log("  --out FILE  Where you write the result");
    console.log("  --long      Do not compress the output");
    console.log("  -?, --help  Show this help");
    console.log("");
    process.exit();
}
 
 
const ruleRegex = /(\$.*?) *: *(\S[\S\s]*?);/g;
var match;
 
const customColors = {};
 
if (parsedArgs.in && fs.existsSync(parsedArgs.in)) {
    let customColorsFile = fs.readFileSync(parsedArgs.in,"utf-8");
    while((match = ruleRegex.exec(customColorsFile)) !== null) {
        customColors[match[1]] = match[2];
    }
}
 
// Load base colours
let colorsFile = fs.readFileSync(path.join(__dirname,"../packages/node_modules/@node-red/editor-client/src/sass/colors.scss"),"utf-8")
let updatedColors = [];
 
while((match = ruleRegex.exec(colorsFile)) !== null) {
    updatedColors.push(match[1]+": "+(customColors[match[1]]||match[2])+";")
}
 
 
(async function() {
    const tmpDir = os.tmpdir();
    const workingDir = await fs.mkdtemp(`${tmpDir}${path.sep}`);
    await fs.copy(path.join(__dirname,"../packages/node_modules/@node-red/editor-client/src/sass/"),workingDir)
    await fs.writeFile(path.join(workingDir,"colors.scss"),updatedColors.join("\n"))
 
    const result = sass.renderSync({
        outputStyle: "expanded",
        file: path.join(workingDir,"style-custom-theme.scss"),
    });
 
    const css = result.css.toString()
    const lines = css.split("\n");
    const colorCSS = []
    const nonColorCSS = [];
 
    let inKeyFrameBlock = false;
 
    lines.forEach(l => {
        if (inKeyFrameBlock) {
            nonColorCSS.push(l);
            if (/^}/.test(l)) {
                inKeyFrameBlock = false;
            }
        } else if (/^@keyframes/.test(l)) {
            nonColorCSS.push(l);
            inKeyFrameBlock = true;
        } else if (!/^  /.test(l)) {
            colorCSS.push(l);
            nonColorCSS.push(l);
        } else if (/color|border|background|fill|stroke|outline|box-shadow/.test(l)) {
            colorCSS.push(l);
        } else {
            nonColorCSS.push(l);
        }
    });
 
    const nrPkg = require("../package.json");
    const now = new Date().toISOString();
 
    const header = `/*
    * Theme generated with Node-RED ${nrPkg.version} on ${now}
    */`;
 
    var output = sass.renderSync({outputStyle: parsedArgs.long?"expanded":"compressed",data:colorCSS.join("\n")});
    if (parsedArgs.out) {
 
        await fs.writeFile(parsedArgs.out,header+"\n"+output.css);
    } else {
        console.log(header);
        console.log(output.css.toString());
    }
    await fs.remove(workingDir);
})()