This commit lays the foundation for migrating OxJS from its custom module
system to ES modules while maintaining backward compatibility.
Key changes:
- Set up npm project with Vite for modern build tooling
- Created ES module versions of core Ox utilities (Type, Collection, DOM, etc.)
- Implemented compatibility layer for legacy Ox.load() pattern
- Added Vitest for testing with initial test suite
- Created script to extract existing inline tests from documentation
- Updated .gitignore for Node.js/npm development
The migration preserves OxJS's innovative inline test system and maintains
backward compatibility. Original source files remain unchanged.
Next steps include migrating UI modules, replacing the Python build script,
and creating npm package distribution.
🤖 Generated with AI assistance
229 lines
No EOL
6.1 KiB
JavaScript
229 lines
No EOL
6.1 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Extract inline tests from OxJS documentation comments
|
|
* and convert them to Vitest test files
|
|
*/
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const { glob } = require('glob');
|
|
|
|
// Regular expressions from Ox.doc
|
|
const re = {
|
|
multiline: /\/\*@([\w\W]+?)@?\*\//g,
|
|
singleline: /\/\/@\s*(.*?)\s*$/gm,
|
|
test: /^\s*>\s+(.+)$/,
|
|
expected: /^\s*([^>].*)$/,
|
|
item: /^(.+?)\s+<(.+?)>\s+(.+?)$/,
|
|
};
|
|
|
|
/**
|
|
* Parse documentation comments from source code
|
|
*/
|
|
function parseDocComments(source, filename) {
|
|
const docs = [];
|
|
let match;
|
|
|
|
// Parse multiline comments
|
|
while ((match = re.multiline.exec(source)) !== null) {
|
|
const content = match[1];
|
|
const doc = parseDocContent(content, filename);
|
|
if (doc) docs.push(doc);
|
|
}
|
|
|
|
// Parse single line comments
|
|
source.replace(re.singleline, (match, content) => {
|
|
const doc = parseDocContent(content, filename);
|
|
if (doc) docs.push(doc);
|
|
return match;
|
|
});
|
|
|
|
return docs;
|
|
}
|
|
|
|
/**
|
|
* Parse documentation content
|
|
*/
|
|
function parseDocContent(content, filename) {
|
|
const lines = content.split('\n');
|
|
const firstLine = lines[0].trim();
|
|
const itemMatch = firstLine.match(re.item);
|
|
|
|
if (!itemMatch) return null;
|
|
|
|
const doc = {
|
|
name: itemMatch[1],
|
|
type: itemMatch[2],
|
|
summary: itemMatch[3],
|
|
file: filename,
|
|
tests: []
|
|
};
|
|
|
|
// Extract tests
|
|
let inTest = false;
|
|
let currentTest = null;
|
|
|
|
for (let i = 1; i < lines.length; i++) {
|
|
const line = lines[i];
|
|
const testMatch = line.match(re.test);
|
|
const expectedMatch = line.match(re.expected);
|
|
|
|
if (testMatch) {
|
|
if (currentTest) {
|
|
doc.tests.push(currentTest);
|
|
}
|
|
currentTest = {
|
|
statement: testMatch[1],
|
|
expected: null
|
|
};
|
|
inTest = true;
|
|
} else if (inTest && expectedMatch) {
|
|
if (currentTest) {
|
|
currentTest.expected = expectedMatch[1].trim();
|
|
doc.tests.push(currentTest);
|
|
currentTest = null;
|
|
inTest = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (currentTest) {
|
|
doc.tests.push(currentTest);
|
|
}
|
|
|
|
return doc.tests.length > 0 ? doc : null;
|
|
}
|
|
|
|
/**
|
|
* Convert extracted tests to Vitest format
|
|
*/
|
|
function generateVitestTest(docs, sourceFile) {
|
|
if (docs.length === 0) return null;
|
|
|
|
const testName = path.basename(sourceFile, '.js');
|
|
const tests = [];
|
|
|
|
tests.push(`import { describe, it, expect, beforeAll } from 'vitest';`);
|
|
tests.push(`import '../test-setup.js';\n`);
|
|
tests.push(`// Tests extracted from ${sourceFile}\n`);
|
|
tests.push(`describe('${testName}', () => {`);
|
|
|
|
for (const doc of docs) {
|
|
if (doc.tests.length === 0) continue;
|
|
|
|
tests.push(` describe('${doc.name}', () => {`);
|
|
|
|
for (const test of doc.tests) {
|
|
if (!test.expected) continue;
|
|
|
|
// Escape the test statement for use in test name
|
|
const testName = test.statement.replace(/'/g, "\\'").substring(0, 60);
|
|
|
|
tests.push(` it('${testName}...', async () => {`);
|
|
tests.push(` const actual = await evaluateInContext(\`${test.statement.replace(/`/g, '\\`')}\`);`);
|
|
tests.push(` const expected = ${test.expected};`);
|
|
tests.push(` expect(actual).toEqual(expected);`);
|
|
tests.push(` });\n`);
|
|
}
|
|
|
|
tests.push(` });\n`);
|
|
}
|
|
|
|
tests.push(`});`);
|
|
|
|
return tests.join('\n');
|
|
}
|
|
|
|
/**
|
|
* Process a single source file
|
|
*/
|
|
async function processFile(filePath) {
|
|
const source = fs.readFileSync(filePath, 'utf-8');
|
|
const docs = parseDocComments(source, filePath);
|
|
|
|
if (docs.length === 0) return null;
|
|
|
|
const testContent = generateVitestTest(docs, filePath);
|
|
if (!testContent) return null;
|
|
|
|
// Create test file path
|
|
const relativePath = path.relative('source', filePath);
|
|
const testPath = path.join('test/extracted', relativePath.replace('.js', '.test.js'));
|
|
|
|
// Ensure directory exists
|
|
fs.mkdirSync(path.dirname(testPath), { recursive: true });
|
|
|
|
// Write test file
|
|
fs.writeFileSync(testPath, testContent);
|
|
|
|
console.log(`✓ Extracted tests from ${relativePath} -> ${testPath}`);
|
|
return testPath;
|
|
}
|
|
|
|
/**
|
|
* Create test setup file
|
|
*/
|
|
function createTestSetup() {
|
|
const setupContent = `/**
|
|
* Test setup for running extracted OxJS inline tests
|
|
*/
|
|
|
|
// Load OxJS in test environment
|
|
import '../source/Ox.js';
|
|
|
|
// Helper function to evaluate test statements in context
|
|
global.evaluateInContext = async function(statement) {
|
|
try {
|
|
// This will need to be enhanced to handle async tests
|
|
// For now, we'll use eval which isn't ideal but matches the original test system
|
|
return eval(statement);
|
|
} catch (error) {
|
|
console.error('Error evaluating:', statement, error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
// Make Ox available globally for tests
|
|
global.Ox = window.Ox || global.Ox;
|
|
`;
|
|
|
|
fs.mkdirSync('test', { recursive: true });
|
|
fs.writeFileSync('test/test-setup.js', setupContent);
|
|
console.log('✓ Created test/test-setup.js');
|
|
}
|
|
|
|
/**
|
|
* Main function
|
|
*/
|
|
async function main() {
|
|
console.log('Extracting inline tests from OxJS source files...\n');
|
|
|
|
// Create test setup
|
|
createTestSetup();
|
|
|
|
// Find all JavaScript files in source
|
|
const files = await glob('source/**/*.js', {
|
|
ignore: ['**/node_modules/**', '**/min/**', '**/dev/**']
|
|
});
|
|
|
|
console.log(`Found ${files.length} source files to process\n`);
|
|
|
|
const testFiles = [];
|
|
for (const file of files) {
|
|
const testFile = await processFile(file);
|
|
if (testFile) {
|
|
testFiles.push(testFile);
|
|
}
|
|
}
|
|
|
|
console.log(`\n✅ Extracted tests from ${testFiles.length} files`);
|
|
console.log('\nRun "npm test" to execute the extracted tests');
|
|
}
|
|
|
|
// Run if called directly
|
|
if (require.main === module) {
|
|
main().catch(console.error);
|
|
}
|
|
|
|
module.exports = { parseDocComments, generateVitestTest }; |