rynex
Version:
A minimalist TypeScript framework for building reactive web applications with no virtual DOM
190 lines • 5.98 kB
JavaScript
/**
* Rynex Parser
* Transforms view and style keywords into runtime calls
*/
/**
* Parse Rynex component file and transform view/style keywords
*/
export function parseRynexFile(source) {
let transformedCode = source;
let extractedStyles = '';
// Extract and transform style blocks using balanced brace matching
transformedCode = extractStyleBlocks(transformedCode, (styleContent, index) => {
const componentId = `zw-${generateHash(source)}-${index}`;
const scopedStyles = scopeStyles(styleContent, componentId);
extractedStyles += scopedStyles + '\n';
});
// Transform view blocks using balanced brace matching
transformedCode = extractViewBlocks(transformedCode);
return {
code: transformedCode,
styles: extractedStyles
};
}
/**
* Extract and remove style blocks
*/
function extractStyleBlocks(code, callback) {
let result = '';
let i = 0;
let styleIndex = 0;
while (i < code.length) {
// Look for 'style {'
const styleMatch = code.substring(i).match(/^\s*style\s*\{/);
if (styleMatch) {
// Add everything before 'style {'
result += code.substring(0, i);
// Find the matching closing brace
const startPos = i + styleMatch[0].length;
const endPos = findMatchingBrace(code, startPos - 1);
if (endPos !== -1) {
const content = code.substring(startPos, endPos);
callback(content, styleIndex++);
// Skip past the style block
code = code.substring(endPos + 1);
i = 0;
continue;
}
}
i++;
}
result += code;
return result;
}
/**
* Extract and transform view blocks
*/
function extractViewBlocks(code) {
let result = '';
let i = 0;
while (i < code.length) {
// Look for 'view {'
const viewMatch = code.substring(i).match(/^\s*view\s*\{/);
if (viewMatch) {
// Add everything before 'view {'
result += code.substring(0, i);
// Find the matching closing brace
const startPos = i + viewMatch[0].length;
const endPos = findMatchingBrace(code, startPos - 1);
if (endPos !== -1) {
const content = code.substring(startPos, endPos).trim();
// Transform to return statement
result += `return (${content});`;
// Skip past the view block
code = code.substring(endPos + 1);
i = 0;
continue;
}
}
i++;
}
result += code;
return result;
}
/**
* Find the matching closing brace for an opening brace
*/
function findMatchingBrace(str, openPos) {
let depth = 1;
let inString = false;
let stringChar = '';
let inTemplate = false;
let templateDepth = 0;
for (let i = openPos + 1; i < str.length; i++) {
const char = str[i];
const prevChar = i > 0 ? str[i - 1] : '';
// Handle string literals
if ((char === '"' || char === "'" || char === '`') && prevChar !== '\\') {
if (!inString) {
inString = true;
stringChar = char;
if (char === '`')
inTemplate = true;
}
else if (char === stringChar) {
inString = false;
if (char === '`')
inTemplate = false;
}
}
// Handle template literal expressions
if (inTemplate && char === '{' && prevChar === '$') {
templateDepth++;
continue;
}
if (inTemplate && char === '}' && templateDepth > 0) {
templateDepth--;
continue;
}
// Skip if we're in a string
if (inString)
continue;
// Count braces
if (char === '{') {
depth++;
}
else if (char === '}') {
depth--;
if (depth === 0) {
return i;
}
}
}
return -1; // No matching brace found
}
/**
* Scope CSS styles to a component
* For now, we'll use a simple class-based scoping approach
*/
function scopeStyles(css, componentId) {
// Simple approach: just return the CSS as-is for now
// TODO: Implement proper scoping with component hash classes
const lines = css.split('\n');
let result = '';
let inBlock = false;
for (const line of lines) {
const trimmed = line.trim();
if (trimmed.includes('{')) {
// Selector line
const selector = trimmed.substring(0, trimmed.indexOf('{')).trim();
result += `${selector} {\n`;
inBlock = true;
// Handle inline properties
const afterBrace = trimmed.substring(trimmed.indexOf('{') + 1).trim();
if (afterBrace) {
result += ` ${afterBrace}\n`;
}
}
else if (trimmed === '}') {
result += '}\n';
inBlock = false;
}
else if (trimmed) {
result += ` ${trimmed}\n`;
}
}
return result;
}
/**
* Generate a simple hash for component identification
*/
function generateHash(str) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash; // Convert to 32-bit integer
}
return Math.abs(hash).toString(36);
}
/**
* Transform imports to use runtime
*/
export function transformImports(code) {
// Replace rynex/runtime imports
code = code.replace(/from\s+['"]rynex\/runtime['"]/g, "from 'rynex/runtime'");
// Replace rynex/helpers imports
code = code.replace(/from\s+['"]rynex\/helpers['"]/g, "from 'rynex/runtime'");
return code;
}
//# sourceMappingURL=parser.js.map