wgsl-plus
Version:
A WGSL preprocessor, prettifier, minifier, obfuscator, and compiler with C-style macros, conditional compilation, file linking, and multi-format output for WebGPU shaders.
72 lines (71 loc) • 3.08 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = minify;
const tokenize_wgsl_1 = __importDefault(require("../tokenization/tokenize-wgsl"));
// Main minification function
function minify(code) {
// Use the comprehensive tokenizer from the obfuscator
const tokens = (0, tokenize_wgsl_1.default)(code).filter(token => token.type !== 'comment');
let output = '';
// First pass: identify struct definitions to remove trailing semicolons
// Looking for pattern: struct [identifier] { ... };
const skipIndices = new Set();
// Find struct definitions and mark their trailing semicolons for removal
for (let i = 0; i < tokens.length - 2; i++) {
// Find struct keyword
if (tokens[i].type === 'keyword' && tokens[i].value === 'struct') {
// Look for the closing pattern: }; (closing brace followed by semicolon)
for (let j = i + 3; j < tokens.length - 1; j++) {
if (tokens[j].type === 'operator' && tokens[j].value === '}' &&
tokens[j + 1].type === 'operator' && tokens[j + 1].value === ';') {
// Mark the semicolon for removal
skipIndices.add(j + 1);
break;
}
}
}
}
// Reconstruct the code, inserting spaces where necessary
for (let i = 0; i < tokens.length; i++) {
// Skip tokens marked for removal
if (skipIndices.has(i)) {
continue;
}
// Check if a space is needed before the current token
if (i > 0) {
const t1 = tokens[i - 1];
const t2 = tokens[i];
if (needsSpace(t1, t2)) {
output += ' ';
}
}
// Append the token's value
output += tokens[i].value;
}
return output;
}
// Helper function to check if a token is identifier-like (identifier, keyword, or builtin)
function isIdentifierLike(token) {
return token.type === 'identifier' || token.type === 'keyword' || token.type === 'builtin';
}
// Helper function to check if two tokens need a space between them
function needsSpace(t1, t2) {
// Between identifier-like tokens or between identifier-like and number
if ((isIdentifierLike(t1) && isIdentifierLike(t2)) ||
(isIdentifierLike(t1) && t2.type === 'number')) {
return true;
}
// Special case for attributes:
// Add space after standalone attributes (like @fragment) before keywords/identifiers
// But don't add space after attributes with parameters (like @location(0))
if (t1.type === 'attribute' && isIdentifierLike(t2)) {
// Check if the attribute has parameters (contains opening parenthesis)
const hasParameters = t1.value.includes('(');
// Only add space for standalone attributes without parameters
return !hasParameters;
}
return false;
}