Closed wtgtybhertgeghgtwtg closed 3 years ago
Interesting! Thank you for doing this, this is really cool.
This dumps the generated graph at build time but still runs everything through the linker, which does the composition upon require. How difficult would it be to basically map all of the functions at build time, too?
How difficult would it be to basically map all of the functions at build time, too?
At the very least, it wouldn't be easy. I think the best I could do would be to get the path
(i.e. ['hex', 'rgb', 'hsl']
or whatever) for each node at build-time, but it'll still have to compose the actual functions. So something like
// `modelPaths` would probably actually be an array to skip the runtime `Object.entries` or whatever.
for (const [fromModel, paths] of Object.entries(modelPaths)) {
convert[fromModel] = {};
Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels});
Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels});
for (const [toModel, path] of Object.entries(paths)) {
// Takes ['hex', 'rgb', 'hsl'] and returns the function chaining that together.
const fn = composeConversion(path)
convert[fromModel][toModel] = wrapRounded(fn);
convert[fromModel][toModel].raw = wrapRaw(fn);
}
}
Updated with a proof of concept of pushing the graph traversal to build time. I want to see if I can cut down on the main loop.
Sorry for the delay.
Updated with prevaling away some of the initial Object.defineProperty
'ing. I think this might be a controversial change, but it sets up the groundwork of tree shaking in a potential major.
I think my point is still being missed. This still builds things up at run time. Why not use the script I mentioned in the other ticket to pre-evaluate the build graph and emit new JavaScript?
If I'm understanding correctly, this does create the build graph. At run time, it's a constant.
const models = [["rgb", {
"channels": 3,
"labels": "rgb",
"paths": [["hsl"], ["hsv"], ["hwb"], ["cmyk"], ["xyz"], ["lab"], ["lab", "lch"], ["hex"], ["keyword"], ["ansi16"], ["ansi256"], ["hcg"], ["apple"], ["gray"]]
}], ["hsl", {
"channels": 3,
"labels": "hsl",
"paths": [["rgb"], ["hsv"], ["hcg", "hwb"], ["rgb", "cmyk"], ["rgb", "xyz"], ["rgb", "lab"], ["rgb", "lab", "lch"], ["rgb", "hex"], ["rgb", "keyword"], ["hsv", "ansi16"], ["rgb", "ansi256"], ["hcg"], ["rgb", "apple"], ["rgb", "gray"]]
}], ["hsv", {
"channels": 3,
"labels": "hsv",
"paths": [["rgb"], ["hsl"], ["hcg", "hwb"], ["rgb", "cmyk"], ["rgb", "xyz"], ["rgb", "lab"], ["rgb", "lab", "lch"], ["rgb", "hex"], ["rgb", "keyword"], ["ansi16"], ["rgb", "ansi256"], ["hcg"], ["rgb", "apple"], ["rgb", "gray"]]
}], ["hwb", {
"channels": 3,
"labels": "hwb",
"paths": [["rgb"], ["hcg", "hsl"], ["hcg", "hsv"], ["rgb", "cmyk"], ["rgb", "xyz"], ["rgb", "lab"], ["rgb", "lab", "lch"], ["rgb", "hex"], ["rgb", "keyword"], ["rgb", "ansi16"], ["rgb", "ansi256"], ["hcg"], ["rgb", "apple"], ["rgb", "gray"]]
}], ["cmyk", {
"channels": 4,
"labels": "cmyk",
"paths": [["rgb"], ["rgb", "hsl"], ["rgb", "hsv"], ["rgb", "hwb"], ["rgb", "xyz"], ["rgb", "lab"], ["rgb", "lab", "lch"], ["rgb", "hex"], ["rgb", "keyword"], ["rgb", "ansi16"], ["rgb", "ansi256"], ["rgb", "hcg"], ["rgb", "apple"], ["rgb", "gray"]]
}], ["xyz", {
"channels": 3,
"labels": "xyz",
"paths": [["rgb"], ["rgb", "hsl"], ["rgb", "hsv"], ["rgb", "hwb"], ["rgb", "cmyk"], ["lab"], ["lab", "lch"], ["rgb", "hex"], ["rgb", "keyword"], ["rgb", "ansi16"], ["rgb", "ansi256"], ["rgb", "hcg"], ["rgb", "apple"], ["rgb", "gray"]]
}], ["lab", {
"channels": 3,
"labels": "lab",
"paths": [["xyz", "rgb"], ["xyz", "rgb", "hsl"], ["xyz", "rgb", "hsv"], ["xyz", "rgb", "hwb"], ["xyz", "rgb", "cmyk"], ["xyz"], ["lch"], ["xyz", "rgb", "hex"], ["xyz", "rgb", "keyword"], ["xyz", "rgb", "ansi16"], ["xyz", "rgb", "ansi256"], ["xyz", "rgb", "hcg"], ["xyz", "rgb", "apple"], ["xyz", "rgb", "gray"]]
}], ["lch", {
"channels": 3,
"labels": "lch",
"paths": [["lab", "xyz", "rgb"], ["lab", "xyz", "rgb", "hsl"], ["lab", "xyz", "rgb", "hsv"], ["lab", "xyz", "rgb", "hwb"], ["lab", "xyz", "rgb", "cmyk"], ["lab", "xyz"], ["lab"], ["lab", "xyz", "rgb", "hex"], ["lab", "xyz", "rgb", "keyword"], ["lab", "xyz", "rgb", "ansi16"], ["lab", "xyz", "rgb", "ansi256"], ["lab", "xyz", "rgb", "hcg"], ["lab", "xyz", "rgb", "apple"], ["lab", "xyz", "rgb", "gray"]]
}], ["hex", {
"channels": 1,
"labels": ["hex"],
"paths": [["rgb"], ["rgb", "hsl"], ["rgb", "hsv"], ["rgb", "hwb"], ["rgb", "cmyk"], ["rgb", "xyz"], ["rgb", "lab"], ["rgb", "lab", "lch"], ["rgb", "keyword"], ["rgb", "ansi16"], ["rgb", "ansi256"], ["rgb", "hcg"], ["rgb", "apple"], ["rgb", "gray"]]
}], ["keyword", {
"channels": 1,
"labels": ["keyword"],
"paths": [["rgb"], ["rgb", "hsl"], ["rgb", "hsv"], ["rgb", "hwb"], ["rgb", "cmyk"], ["rgb", "xyz"], ["rgb", "lab"], ["rgb", "lab", "lch"], ["rgb", "hex"], ["rgb", "ansi16"], ["rgb", "ansi256"], ["rgb", "hcg"], ["rgb", "apple"], ["rgb", "gray"]]
}], ["ansi16", {
"channels": 1,
"labels": ["ansi16"],
"paths": [["rgb"], ["rgb", "hsl"], ["rgb", "hsv"], ["rgb", "hwb"], ["rgb", "cmyk"], ["rgb", "xyz"], ["rgb", "lab"], ["rgb", "lab", "lch"], ["rgb", "hex"], ["rgb", "keyword"], ["rgb", "ansi256"], ["rgb", "hcg"], ["rgb", "apple"], ["rgb", "gray"]]
}], ["ansi256", {
"channels": 1,
"labels": ["ansi256"],
"paths": [["rgb"], ["rgb", "hsl"], ["rgb", "hsv"], ["rgb", "hwb"], ["rgb", "cmyk"], ["rgb", "xyz"], ["rgb", "lab"], ["rgb", "lab", "lch"], ["rgb", "hex"], ["rgb", "keyword"], ["rgb", "ansi16"], ["rgb", "hcg"], ["rgb", "apple"], ["rgb", "gray"]]
}], ["hcg", {
"channels": 3,
"labels": ["h", "c", "g"],
"paths": [["rgb"], ["hsl"], ["hsv"], ["hwb"], ["rgb", "cmyk"], ["rgb", "xyz"], ["rgb", "lab"], ["rgb", "lab", "lch"], ["rgb", "hex"], ["rgb", "keyword"], ["hsv", "ansi16"], ["rgb", "ansi256"], ["rgb", "apple"], ["rgb", "gray"]]
}], ["apple", {
"channels": 3,
"labels": ["r16", "g16", "b16"],
"paths": [["rgb"], ["rgb", "hsl"], ["rgb", "hsv"], ["rgb", "hwb"], ["rgb", "cmyk"], ["rgb", "xyz"], ["rgb", "lab"], ["rgb", "lab", "lch"], ["rgb", "hex"], ["rgb", "keyword"], ["rgb", "ansi16"], ["rgb", "ansi256"], ["rgb", "hcg"], ["rgb", "gray"]]
}], ["gray", {
"channels": 1,
"labels": ["gray"],
"paths": [["rgb"], ["hsl"], ["hsv"], ["hwb"], ["cmyk"], ["lab", "xyz"], ["lab"], ["lab", "lch"], ["hex"], ["rgb", "keyword"], ["hsv", "ansi16"], ["rgb", "ansi256"], ["hsl", "hcg"], ["rgb", "apple"]]
}]];
It's enumerated at run time as the functions are created, though.
for (const [fromModel, {
channels,
labels,
paths
}] of models) {
convert[fromModel] = {};
Object.defineProperty(convert[fromModel], 'channels', {
value: channels
});
Object.defineProperty(convert[fromModel], 'labels', {
value: labels
});
for (const path of paths) {
const toModel = path[path.length - 1];
const fn = composeConversion([fromModel, ...path]);
convert[fromModel][toModel] = wrapRounded(fn);
convert[fromModel][toModel].raw = wrapRaw(fn);
}
}
Nevermind, I'm dumb. Locally, I got it to generate output like
const hwbansi16fn = args => rgb.ansi16(hwb.rgb(args));
convert.hwb.ansi16 = wrapRounded(hwbansi16fn);
convert.hwb.ansi16.raw = wrapRaw(hwbansi16fn);
const hwbansi256fn = args => rgb.ansi256(hwb.rgb(args));
convert.hwb.ansi256 = wrapRounded(hwbansi256fn);
convert.hwb.ansi256.raw = wrapRaw(hwbansi256fn);
const hwbhcgfn = args => hwb.hcg(args);
convert.hwb.hcg = wrapRounded(hwbhcgfn);
convert.hwb.hcg.raw = wrapRaw(hwbhcgfn);
const hwbapplefn = args => rgb.apple(hwb.rgb(args));
convert.hwb.apple = wrapRounded(hwbapplefn);
convert.hwb.apple.raw = wrapRaw(hwbapplefn);
const hwbgrayfn = args => rgb.gray(hwb.rgb(args));
convert.hwb.gray = wrapRounded(hwbgrayfn);
convert.hwb.gray.raw = wrapRaw(hwbgrayfn);
convert.cmyk = {};
Object.defineProperty(convert.cmyk, 'channels', {
value: 4
});
Object.defineProperty(convert.cmyk, 'labels', {
value: 'cmyk'
});
I don't think it's particularly stable, but is that what you meant?
Restarted everyone from master to make the diff less horrific. I got it to a relatively low number of lines of code. Setting up the functions is now inline.
convert.hwb = {
rgb: wrapFn(hwb.rgb),
hsl: wrapFn(args => hcg.hsl(hwb.hcg(args))),
hsv: wrapFn(args => hcg.hsv(hwb.hcg(args))),
cmyk: wrapFn(args => rgb.cmyk(hwb.rgb(args))),
xyz: wrapFn(args => rgb.xyz(hwb.rgb(args))),
lab: wrapFn(args => rgb.lab(hwb.rgb(args))),
lch: wrapFn(args => lab.lch(rgb.lab(hwb.rgb(args)))),
hex: wrapFn(args => rgb.hex(hwb.rgb(args))),
keyword: wrapFn(args => rgb.keyword(hwb.rgb(args))),
ansi16: wrapFn(args => rgb.ansi16(hwb.rgb(args))),
ansi256: wrapFn(args => rgb.ansi256(hwb.rgb(args))),
hcg: wrapFn(hwb.hcg),
apple: wrapFn(args => rgb.apple(hwb.rgb(args))),
gray: wrapFn(args => rgb.gray(hwb.rgb(args)))
};
Object.defineProperty(convert.hwb, 'channels', {
value: 3
});
Object.defineProperty(convert.hwb, 'labels', {
value: ['hwb']
});
Hi there, sorry for the delay. I'm writing an Acorn-based build script for this right now, which will be a bit lighter and faster and achieve a faster startup time overall.
I appreciate the work done here, though :)
Builds off #59.