Files
fluent/.config/generateSwizzle.js
2026-07-11 01:46:43 +02:00

166 lines
3.9 KiB
JavaScript

import { writeFileSync } from "fs";
import { relative } from "path";
function capitalize(/** @type {string} */ s) {
return s.slice(0, 1).toUpperCase() + s.slice(1).toLowerCase();
}
const script = import.meta.filename;
const path = "./src/mixin/math/swizzlePermutations.d.ts";
const axis = ["x", "y", "z", "w", "v"];
const property = ["first", "second", "third", "fourth", "fifth"];
const mixin = "./src/mixin/base/mixin";
let conditional = "";
for (let n = 0; n < axis.length; n++) {
if (n === axis.length - 1) {
conditional += `Swizzle${n + 1}D<T, t>`;
break;
}
const type = new Array(n + 1).fill("number").join(", ");
const indent = "\t".repeat(n + 1);
conditional += `T extends readonly [${type}]\n`;
conditional += `${indent}? Swizzle${n + 1}D<T, t>\n`;
conditional += `${indent}: `;
}
const type = new Array(axis.length).fill("number").join(", ");
let src = `
// .d.ts file auto-generated by ${relative(path, script)}
// Do not edit
import type { Props, Return } from '${relative(path, mixin)}';
type IsValid<T extends readonly number[]> = number extends T['length']
? false
: T extends readonly []
? false
: true;
export type SwizzleProps<T extends readonly number[], t extends Props> =
IsValid<T> extends false
? {}
: ${conditional
.split("\n")
.map((line) => "\t\t" + line)
.join("\n")
.trimStart()};
export interface ISwizzle<
T extends readonly [${type}] =
readonly [${type}],
t extends Props = Props
> extends Swizzle${axis.length}D<T, t> {};
`.trimStart();
for (let n = 0; n < axis.length; n++) {
const state = new Array(axis.length).fill(-1);
const props = [];
outer: do {
for (let i = state.length - 1; i >= 0; i--)
if (++state[i] > n) {
state[i] = -1;
} else break;
let nulled = false;
for (const item of state) {
if (item === -1) {
nulled = true;
continue;
}
if (nulled) continue outer;
}
if (state.every((x) => x === -1)) break;
const key = state
.filter((x) => x !== -1)
.map((x) => axis[x])
.join("");
if (state.slice(1).every((x) => x === -1)) {
const arr = [5, 3, 8, 2, 1].map((x) =>
Math.abs(x - n - state[0]),
);
props.push({
doc: `
/**
* Vector-style accessor, ${property[state[0]]} property of the value
* @from {@link Math \`Math\`}
* @example
* \`\`\`ts
* const vec = [${arr
.slice(0, Math.max(n + 1, 2))
.map((n) => n.toString())
.join(", ")}] as const;
*
* const ${axis[state[0]]} = $(vec).${axis[state[0]]}.value;
* expect(${axis[state[0]]}).toBe(${arr[state[0]]})
* \`\`\`
*/`.trimStart(),
property: `readonly ${key}: Return<T[${state[0]}], t>`,
});
continue;
}
let count = 0;
if (state.every((x) => x === count++)) continue;
const arr = [6, 2, 3, 5, 4, 7].map((x, idx) =>
Math.abs(x - state[idx]),
);
const type = state
.filter((x) => x !== -1)
.map((x) => `T[${x}]`)
.join(", ");
props.push({
doc: `
/**
* Vector-style swizzle accessor, rearannges the elements of the value
*
* Equivalent to \`[${state
.filter((x) => x !== -1)
.map((x) => `vec[${x}]`)
.join(", ")}]\`
* @from {@link Math \`Math\`}
* @example
* \`\`\`ts
* const vec = [${arr.slice(0, n + 1).join(", ")}] as const;
*
* const ${key} = $(vec).${key}().value;
* expect(${key}).toMatchObject([${state
.filter((x) => x !== -1)
.map((x) => arr[x])
.join(", ")}])
* \`\`\`
*/
`.trimStart(),
property: `readonly ${key}: () => Return<[${type}], t>`,
});
} while (state.some((x) => x !== -1));
props.sort((a, b) => a.property.length - b.property.length);
src += `interface Swizzle${n + 1}D<T extends readonly [${new Array(n + 1).fill("number").join(", ")}], t extends Props> {\n`;
src += `${props
.map((x) =>
[x.doc, x.property]
.join("\n")
.split("\n")
.map((line) => "\t" + line)
.join("\n"),
)
.join("\n")}\n`;
src += "}\n";
src += "\n";
}
writeFileSync(path, src);