multi-framework-component-transpiler
Version:
Transpile components to multiple frameworks
284 lines (234 loc) • 7.63 kB
JavaScript
/**
* Multi-framework Component Transpiler
*
* This script reads an input file (JavaScript, TypeScript, JSX, TSX, Vue, or Svelte)
* and generates transpiled versions in multiple frameworks and formats:
*
* - file.tsx (TypeScript React)
* - file.jsx (JavaScript React)
* - file.js (Vanilla JavaScript ESNext)
* - file.es5.js (Vanilla JavaScript ES5)
* - file.ts (TypeScript)
* - file.svelte (JavaScript Svelte)
* - file.ts.svelte (TypeScript Svelte)
* - file.vue (JavaScript Vue)
* - file.ts.vue (TypeScript Vue)
* - angular_folder/ (Angular component in TypeScript)
*/
const fs = require("fs");
const path = require("path");
const babelParser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const generator = require("@babel/generator").default;
const vueCompiler = require("vue-template-compiler");
const svelte = require("svelte/compiler");
// Read command-line arguments
const inputFile = process.argv[2];
const outputDir = process.argv[3] || "./transpiled-components";
// Check if input file is provided
if (!inputFile) {
console.error("Usage: node transpiler.js <inputFile> [outputDir]");
process.exit(1);
}
// Check if input file exists
if (!fs.existsSync(inputFile)) {
console.error(`Input file "${inputFile}" does not exist.`);
process.exit(1);
}
// Read the input file
const code = fs.readFileSync(inputFile, "utf-8");
// Determine the input file type based on extension
const ext = path.extname(inputFile).toLowerCase();
let globalCode = "";
// Function to extract global code from AST
function extractGlobalCode(ast) {
const globalIdentifiers = ["window", "document", "localStorage"];
const globalCodeNodes = [];
traverse(ast, {
enter(path) {
if (
path.isIdentifier() &&
globalIdentifiers.includes(path.node.name) &&
!path.scope.hasBinding(path.node.name)
) {
const statement = path.getStatementParent().node;
globalCodeNodes.push(statement);
}
},
});
// Remove duplicates
const uniqueNodes = Array.from(new Set(globalCodeNodes));
// Generate code from AST nodes
const globalCode = uniqueNodes.map((node) => generator(node).code).join("\n");
return globalCode;
}
// Parse the input code and extract global code
try {
let ast;
switch (ext) {
case ".js":
case ".jsx":
case ".ts":
case ".tsx":
ast = babelParser.parse(code, {
sourceType: "module",
plugins: ["jsx", "typescript"],
});
globalCode = extractGlobalCode(ast);
break;
case ".vue":
const parsedVue = vueCompiler.parseComponent(code);
if (parsedVue.script && parsedVue.script.content) {
ast = babelParser.parse(parsedVue.script.content, {
sourceType: "module",
plugins: ["jsx", "typescript"],
});
globalCode = extractGlobalCode(ast);
}
break;
case ".svelte":
// Extract script content manually
const scriptMatch = code.match(/<script[^>]*>([\s\S]*?)<\/script>/);
if (scriptMatch && scriptMatch[1]) {
ast = babelParser.parse(scriptMatch[1], {
sourceType: "module",
plugins: ["jsx", "typescript"],
});
globalCode = extractGlobalCode(ast);
}
break;
default:
console.error(`Unsupported file extension "${ext}"`);
process.exit(1);
}
} catch (error) {
console.error("Error parsing input file:", error.message);
process.exit(1);
}
// Ensure output directory exists
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
// Helper functions to generate code for each framework
// React Component
function generateReactComponent(globalCode, isTypeScript) {
const useEffectHook = `
useEffect(()${isTypeScript ? ": void" : ""} => {
${globalCode}
}, []);
`;
const component = `
import React, { useEffect } from 'react';
function MyComponent()${isTypeScript ? ": JSX.Element" : ""} {
${useEffectHook}
return <div>My Component</div>;
}
export default MyComponent;
`;
return component.trim();
}
// Vue Component
function generateVueComponent(globalCode, isTypeScript) {
const scriptLang = isTypeScript ? 'lang="ts"' : "";
const onMountedHook = `
onMounted(()${isTypeScript ? ": void" : ""} => {
${globalCode}
});
`;
const component = `
<template>
<div>My Component</div>
</template>
<script setup ${scriptLang}>
import { onMounted } from 'vue';
${onMountedHook}
</script>
`;
return component.trim();
}
// Svelte Component
function generateSvelteComponent(globalCode, isTypeScript) {
const scriptLang = isTypeScript ? 'lang="ts"' : "";
const onMountHook = `
onMount(()${isTypeScript ? ": void" : ""} => {
${globalCode}
});
`;
const component = `
<script ${scriptLang}>
import { onMount } from 'svelte';
${onMountHook}
</script>
<div>My Component</div>
`;
return component.trim();
}
// Angular Component
function generateAngularComponent(globalCode) {
const component = `
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-my-component',
template: \`
<div>My Component</div>
\`,
})
export class MyComponent implements OnInit {
ngOnInit(): void {
${globalCode}
}
}
`;
return component.trim();
}
// Vanilla JavaScript
function generateVanillaJS(globalCode, version, isTypeScript) {
let codeWrapper;
if (version === "ES5") {
codeWrapper = `(function() {
${globalCode}
})();`;
} else {
codeWrapper = `(()${isTypeScript ? ": void" : ""} => {
${globalCode}
})();`;
}
return codeWrapper.trim();
}
// Write the generated code to files
// React JSX (JavaScript)
const reactJSX = generateReactComponent(globalCode, false);
fs.writeFileSync(path.join(outputDir, "file.jsx"), reactJSX);
// React TSX (TypeScript)
const reactTSX = generateReactComponent(globalCode, true);
fs.writeFileSync(path.join(outputDir, "file.tsx"), reactTSX);
// Vue JavaScript
const vueJS = generateVueComponent(globalCode, false);
fs.writeFileSync(path.join(outputDir, "file.vue"), vueJS);
// Vue TypeScript
const vueTS = generateVueComponent(globalCode, true);
fs.writeFileSync(path.join(outputDir, "file.ts.vue"), vueTS);
// Svelte JavaScript
const svelteJS = generateSvelteComponent(globalCode, false);
fs.writeFileSync(path.join(outputDir, "file.svelte"), svelteJS);
// Svelte TypeScript
const svelteTS = generateSvelteComponent(globalCode, true);
fs.writeFileSync(path.join(outputDir, "file.ts.svelte"), svelteTS);
// Angular TypeScript
const angularTS = generateAngularComponent(globalCode);
const angularDir = path.join(outputDir, "angular_folder");
if (!fs.existsSync(angularDir)) {
fs.mkdirSync(angularDir);
}
fs.writeFileSync(path.join(angularDir, "my-component.component.ts"), angularTS);
// Vanilla JavaScript ESNext
const vanillaJS = generateVanillaJS(globalCode, "ESNext", false);
fs.writeFileSync(path.join(outputDir, "file.js"), vanillaJS);
// Vanilla JavaScript ES5
const vanillaES5 = generateVanillaJS(globalCode, "ES5", false);
fs.writeFileSync(path.join(outputDir, "file.es5.js"), vanillaES5);
// Vanilla TypeScript
const vanillaTS = generateVanillaJS(globalCode, "ESNext", true);
fs.writeFileSync(path.join(outputDir, "file.ts"), vanillaTS);
console.log("Transpilation completed. Files generated in:", outputDir);