From 0806f1aa92d3d39f470789cda888ca8a59a1eb09 Mon Sep 17 00:00:00 2001 From: GiulianoGosty Date: Fri, 15 Dec 2023 16:38:58 +0100 Subject: [PATCH] temporary fix --- .gitignore | 4 +- cmd/make-js-component.mjs | 18 ++++ package.json | 2 +- src/utils/configs.cjs | 35 ++++++++ src/utils/frameworks/angular/angular.mjs | 9 ++ .../angular/make-angular-component.mjs | 33 +++++++ src/utils/frameworks/astro/astro.mjs | 9 ++ src/utils/frameworks/qwik/qwik.mjs | 27 ++++++ src/utils/frameworks/react/react.mjs | 50 +++++++++++ src/utils/frameworks/svelte/svelte.mjs | 21 +++++ src/utils/frameworks/vue/helper.mjs | 66 ++++++++++++++ src/utils/frameworks/vue/vue.mjs | 26 ++++++ src/utils/utils.mjs | 78 +++++++++++++++++ src/utils/wizard.mjs | 87 +++++++++++++++++++ 14 files changed, 461 insertions(+), 4 deletions(-) create mode 100644 cmd/make-js-component.mjs create mode 100644 src/utils/configs.cjs create mode 100644 src/utils/frameworks/angular/angular.mjs create mode 100644 src/utils/frameworks/angular/make-angular-component.mjs create mode 100644 src/utils/frameworks/astro/astro.mjs create mode 100644 src/utils/frameworks/qwik/qwik.mjs create mode 100644 src/utils/frameworks/react/react.mjs create mode 100644 src/utils/frameworks/svelte/svelte.mjs create mode 100644 src/utils/frameworks/vue/helper.mjs create mode 100644 src/utils/frameworks/vue/vue.mjs create mode 100644 src/utils/utils.mjs create mode 100644 src/utils/wizard.mjs diff --git a/.gitignore b/.gitignore index 2bf3322..997a1c2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ .vscode/ node_modules components -src/stubs/angular/*.js -*/**/*.*js -**/*.mjs \ No newline at end of file +src/stubs/angular/*.js \ No newline at end of file diff --git a/cmd/make-js-component.mjs b/cmd/make-js-component.mjs new file mode 100644 index 0000000..1b2b682 --- /dev/null +++ b/cmd/make-js-component.mjs @@ -0,0 +1,18 @@ +#! /usr/bin/env node +import createComponent from "../src/utils/utils.mjs"; +import wizard from "../src/utils/wizard.mjs"; +var vueApi; +(function (vueApi) { + vueApi["Composition"] = "composition"; + vueApi["Option"] = "option"; +})(vueApi || (vueApi = {})); +wizard() + .then((answers) => { + const { componentName, framework, template, folder, advancedOpts, advanced } = answers; + const api = template.indexOf("composition") !== -1 ? vueApi.Composition : vueApi.Option; + const t = advanced ? "advanced-component.vue" : template; + createComponent(componentName, framework, t, folder, api, advancedOpts); +}) + .catch((e) => { + console.error(e.message); +}); diff --git a/package.json b/package.json index d962b44..43e8b9a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "make-js-component", - "version": "0.2.6beta-.3", + "version": "0.2.6beta-.4", "description": "Easily create your js framework component in one command", "repository": "https://github.com/Giuliano1993/make-js-component", "bin": "./cmd/make-js-component.mjs", diff --git a/src/utils/configs.cjs b/src/utils/configs.cjs new file mode 100644 index 0000000..011d616 --- /dev/null +++ b/src/utils/configs.cjs @@ -0,0 +1,35 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.configs = void 0; +const path = __importStar(require("node:path")); +const mainFilename = path.dirname(module?.filename || ""); +const dir = path.join(mainFilename, "../.."); +exports.configs = { + INIT_PATH: dir, + BASE_DIR: "./src", + STUBS_DIR: "stubs", + COMPONENT_FOLDER: "/components", +}; diff --git a/src/utils/frameworks/angular/angular.mjs b/src/utils/frameworks/angular/angular.mjs new file mode 100644 index 0000000..e59d9f3 --- /dev/null +++ b/src/utils/frameworks/angular/angular.mjs @@ -0,0 +1,9 @@ +const framework = "angular"; +export default function (componentName, folder) { + return { + componentName: componentName, + framework: framework.toLowerCase(), + template: "component.component.ts", + folder: folder, + }; +} diff --git a/src/utils/frameworks/angular/make-angular-component.mjs b/src/utils/frameworks/angular/make-angular-component.mjs new file mode 100644 index 0000000..b5e7cc6 --- /dev/null +++ b/src/utils/frameworks/angular/make-angular-component.mjs @@ -0,0 +1,33 @@ +import * as fs from "fs"; +import path from "path"; +import { configs } from "../../configs.cjs"; +import { writeFile } from "../../utils.mjs"; +export function makeAngularComponent(filePathDestination, component, componentName) { + let componentContent = component.replace(/selector: 'SelectorName'/, `selector: 'app-${convertFromCamelCase(componentName)}'`); + componentContent = replaceComponentName(componentContent, componentName); + writeFile(filePathDestination, componentContent); + makeAngularComponentTest(componentName); +} +function makeAngularComponentTest(componentName) { + const templateFileTestPath = path.join(configs.INIT_PATH, "src", configs.STUBS_DIR, "angular", "component.component.spec.ts"); + fs.readFile(templateFileTestPath, "utf8", (err, component) => { + const componentContent = replaceComponentName(component, componentName); + const filePathDestination = path.join(configs.BASE_DIR, configs.COMPONENT_FOLDER, `${componentName}.component.spec.ts`); + writeFile(filePathDestination, componentContent); + }); +} +function convertToCamelCase(string) { + return string + .replace(/-([a-z])/g, (s) => { + return s.toUpperCase(); + }) + .replace(/^[a-z]/, s => { + return s.toUpperCase(); + }); +} +function convertFromCamelCase(string) { + return string.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(); +} +function replaceComponentName(data, componentName) { + return data.replace(/ComponentName/g, `${convertToCamelCase(componentName)}Component`); +} diff --git a/src/utils/frameworks/astro/astro.mjs b/src/utils/frameworks/astro/astro.mjs new file mode 100644 index 0000000..87af94a --- /dev/null +++ b/src/utils/frameworks/astro/astro.mjs @@ -0,0 +1,9 @@ +const framework = "astro"; +export default function (componentName, folder) { + return { + componentName: componentName, + framework: framework.toLowerCase(), + template: "component.astro", + folder: folder, + }; +} diff --git a/src/utils/frameworks/qwik/qwik.mjs b/src/utils/frameworks/qwik/qwik.mjs new file mode 100644 index 0000000..89d43ac --- /dev/null +++ b/src/utils/frameworks/qwik/qwik.mjs @@ -0,0 +1,27 @@ +import inquirer from "inquirer"; +const framework = "qwik"; +export default function (componentName, folder) { + return inquirer + .prompt([ + { + type: "list", + name: "type", + message: "Choose wich type of component to create", + choices: ["Hello World", "useStore", "useStyles"], + }, + ]) + .then(answers => { + return { + componentName: componentName, + framework: framework.toLowerCase(), + template: answers.type === "Hello World" + ? "hello-world-component.tsx" + : answers.type === "useStore" + ? "usestore-component.tsx" + : answers.type === "useStyles" + ? "usestyles-component.tsx" + : "hello-world-component.tsx", + folder: folder, + }; + }); +} diff --git a/src/utils/frameworks/react/react.mjs b/src/utils/frameworks/react/react.mjs new file mode 100644 index 0000000..bf8c839 --- /dev/null +++ b/src/utils/frameworks/react/react.mjs @@ -0,0 +1,50 @@ +import inquirer from "inquirer"; +const framework = "react"; +export default function (componentName, folder) { + return inquirer + .prompt([ + { + type: "confirm", + name: "typescript", + message: "Do you want to use Typescript?", + default: true, + }, + ]) + .then((answers) => { + const { typescript } = answers; + return inquirer + .prompt([ + { + type: "list", + name: "css", + message: "Do you want to use any CSS framework?", + choices: ["Tailwind", "Styled Components", "CSS Module", "No"], + }, + ]) + .then((answers) => { + const { css } = answers; + const extension = typescript ? "tsx" : "jsx"; + let templateBase = "function-component"; + switch (css) { + case "Tailwind": + templateBase += "-tailwind"; + break; + case "Styled Components": + templateBase += "-styled"; + break; + case "CSS Module": + templateBase += "-css-module"; + break; + default: + break; + } + const template = `${templateBase}.${extension}`; + return { + componentName: componentName, + framework: framework.toLowerCase(), + template: template, + folder: folder, + }; + }); + }); +} diff --git a/src/utils/frameworks/svelte/svelte.mjs b/src/utils/frameworks/svelte/svelte.mjs new file mode 100644 index 0000000..37178ba --- /dev/null +++ b/src/utils/frameworks/svelte/svelte.mjs @@ -0,0 +1,21 @@ +import inquirer from "inquirer"; +const framework = "svelte"; +export default function (componentName, folder) { + return inquirer + .prompt([ + { + type: "confirm", + name: "typescript", + message: "Do you want to use Typescript?", + default: true, + }, + ]) + .then((answers) => { + return { + componentName: componentName, + framework: framework.toLowerCase(), + template: answers.typescript ? "component-ts.svelte" : "component-js.svelte", + folder: folder, + }; + }); +} diff --git a/src/utils/frameworks/vue/helper.mjs b/src/utils/frameworks/vue/helper.mjs new file mode 100644 index 0000000..d1f4a81 --- /dev/null +++ b/src/utils/frameworks/vue/helper.mjs @@ -0,0 +1,66 @@ +export var vueApi; +(function (vueApi) { + vueApi["Composition"] = "composition"; + vueApi["Option"] = "option"; +})(vueApi || (vueApi = {})); +export default function advancedVueBuilder(data, componentType, advancedOpts) { + if (typeof advancedOpts === "undefined") + return ""; + let output = data; + if (componentType === vueApi.Composition) { + const replacable = { + props: "const props = defineProps(['foo'])", + emits: "const emit = defineEmits(['inFocus', 'submit'])", + refs: "const element = ref(null)", + mounted: `onMounted(() => { + console.log("the component is now mounted.") + })`, + data: "", + components: "", + }; + const importsFunctions = []; + for (const key in replacable) { + const codeInject = advancedOpts.indexOf(key) !== -1 ? replacable[key] : ""; + const replacePattern = `__${key}__`; + output = output.replaceAll(replacePattern, codeInject); + if (key === "refs" && advancedOpts.indexOf(key) !== -1) { + importsFunctions.push("ref"); + } + else if (key === "mounted" && advancedOpts.indexOf(key) !== -1) { + importsFunctions.push("onMounted"); + } + } + let imports = ""; + if (importsFunctions.length > 0) { + imports = `import { ${importsFunctions.join(", ")} } from 'vue'`; + } + output = output.replace("__refimport__", imports); + } + else if (componentType === vueApi.Option) { + const replacable = { + props: "props: ['foo'],", + emits: "emits: ['inFocus', 'submit'],", + data: "data:{},", + mounted: "mounted(){},", + refs: "", + components: "components: {},", + }; + for (const key in replacable) { + const codeInject = advancedOpts.indexOf(key) !== -1 ? replacable[key] : ""; + const replacePattern = `__${key}__`; + output = output.replaceAll(replacePattern, codeInject); + } + } + output = cleanVueData(output, componentType); + return output; +} +function cleanVueData(data, api) { + const apiStart = api === vueApi.Composition ? "__compositionstart__" : "__optionsstart__"; + const apiEnd = api === vueApi.Composition ? "__compositionend__" : "__optionsend__"; + const deleteStart = api === vueApi.Composition ? "__optionsstart__" : "__compositionstart__"; + const deleteEnd = api === vueApi.Composition ? "__optionsend__" : "__compositionend__"; + const output = data.replace(apiStart, "").replace(apiEnd, ""); + const start = output.indexOf(deleteStart); + const end = output.indexOf(deleteEnd); + return output.slice(0, start) + output.slice(end + deleteEnd.length); +} diff --git a/src/utils/frameworks/vue/vue.mjs b/src/utils/frameworks/vue/vue.mjs new file mode 100644 index 0000000..ba88e5d --- /dev/null +++ b/src/utils/frameworks/vue/vue.mjs @@ -0,0 +1,26 @@ +import inquirer from "inquirer"; +import { prepareAdvanced } from "../../utils.mjs"; +const framework = "vue"; +export default function (componentName, folder) { + return inquirer + .prompt([ + { + type: "list", + name: "api", + message: "Choose wich api to use", + choices: ["Composition", "Options"], + }, + ...prepareAdvanced(["props", "refs", "data", "mounted", "emits", "components"]), + ]) + .then((answers) => { + return { + componentName: componentName, + framework: framework, + template: answers.api === "Composition" ? "component-composition.vue" : "component-options.vue", + folder: folder, + advanced: answers.advanced, + api: answers.api.toLocaleLowerCase(), + advancedOpts: answers.advancedOpts || [], + }; + }); +} diff --git a/src/utils/utils.mjs b/src/utils/utils.mjs new file mode 100644 index 0000000..c31ce9e --- /dev/null +++ b/src/utils/utils.mjs @@ -0,0 +1,78 @@ +import * as fs from "fs"; +import * as path from "node:path"; +import { configs } from "./configs.cjs"; +import { makeAngularComponent } from "./frameworks/angular/make-angular-component.mjs"; +import advancedVueBuilder from "./frameworks/vue/helper.mjs"; +const createComponent = (componentName, framework, template, customFolder, api, advancedOpts) => { + const destinationFolder = `${configs.BASE_DIR}${configs.COMPONENT_FOLDER}`; + if (!fs.existsSync(destinationFolder)) { + fs.mkdirSync(destinationFolder); + } + const templateFilePath = path.join(configs.INIT_PATH, "src", configs.STUBS_DIR, framework, template); + fs.readFile(templateFilePath, "utf8", (err, data) => { + const customDestinationFolder = path.join(configs.BASE_DIR, configs.COMPONENT_FOLDER, customFolder); + const extension = template.substring(template.indexOf(".")); + const compFileName = `${componentName}${extension}`; + if (!fs.existsSync(customDestinationFolder)) { + fs.mkdirSync(customDestinationFolder, { recursive: true }); + } + const filePathDestination = path.join(configs.BASE_DIR, configs.COMPONENT_FOLDER, customFolder, compFileName); + let output = data; + if (framework === "angular") { + makeAngularComponent(filePathDestination, output, componentName); + } + else { + if (template.indexOf("advanced") !== -1) { + switch (framework) { + case "vue": + output = advancedVueBuilder(output, api, advancedOpts); + break; + default: + break; + } + } + output = output.replaceAll("ComponentName", capitalizeFirstLetter(componentName)); + writeFile(filePathDestination, output); + } + if (path.parse(template).name === "function-component-css-module") { + const styleFileName = `${componentName}.module.css`; + const styleFilePathDestination = path.join(configs.BASE_DIR, configs.COMPONENT_FOLDER, customFolder, styleFileName); + writeFile(styleFilePathDestination, `.${componentName} {\n\tfont-size: 1.125rem; /* 18px */\n\tline-height: 1.75rem; /* 28px */\n\tfont-weight: bold;\n}\n`); + } + }); +}; +export default createComponent; +export function writeFile(filePathDestination, data) { + fs.writeFile(filePathDestination, data, (err) => { + if (err) { + console.error(err); + } + else { + console.log(`✅ CREATE Component: ${filePathDestination}`); + } + }); +} +export function capitalizeFirstLetter(string) { + return string.charAt(0).toUpperCase() + string.slice(1); +} +export function prepareAdvanced(options) { + const arr = [ + { + type: "confirm", + name: "advanced", + message: "Do you want to check for advanced otpions?", + default: false, + }, + { + type: "checkbox", + name: "advancedOpts", + message: "Pick the parts you want in your component?", + choices: options, + when: (answers) => { + return answers.advanced; + }, + default: false, + }, + ]; + return [...arr]; +} diff --git a/src/utils/wizard.mjs b/src/utils/wizard.mjs new file mode 100644 index 0000000..05b3eee --- /dev/null +++ b/src/utils/wizard.mjs @@ -0,0 +1,87 @@ +import { Command } from "commander"; +import inquirer from "inquirer"; +import angularWizard from "./frameworks/angular/angular.mjs"; +import astroWizard from "./frameworks/astro/astro.mjs"; +import qwikWizard from "./frameworks/qwik/qwik.mjs"; +import reactWizard from "./frameworks/react/react.mjs"; +import svelteWizard from "./frameworks/svelte/svelte.mjs"; +import vueWizard from "./frameworks/vue/vue.mjs"; +import { capitalizeFirstLetter } from "./utils.mjs"; +const program = new Command(); +const wizard = async () => { + // Parse command line arguments using commander + const frameworks = ["Vue", "Angular", "React", "Svelte", "Qwik", "Astro"]; + program + .option("--name ", "Specify a name") + .option("-f, --framework ", `Specify framework [${frameworks.join("|")}]`) + .option("--folder ", "Specify the subfolder") + .parse(process.argv); + const options = program.opts(); + const componentNameFromFlag = options.name || ""; + const frameworkFromFlag = options.framework || ""; + const folderFromFlag = options.folder || ""; + const prompts = []; + // Only ask for componentName if --name argument is not provided + if (!componentNameFromFlag) { + prompts.push({ + type: "input", + name: "componentName", + message: "Give a name to your component", + validate: (input) => { + const trimmedInput = input.trim(); + if (trimmedInput === "") { + return "Component name cannot be empty"; + } + // Use a regular expression to check for only alphanumeric characters + const isValid = /^[A-Za-z0-9]+(-[A-Za-z0-9]+)*$/.test(trimmedInput); + return isValid || "Component name can only contain alphanumeric characters"; + }, + }); + } + if (!folderFromFlag) { + prompts.push({ + type: "input", + name: "folder", + message: "Custom path for the component (default: src/components)", + default: "", + }); + } + if (!frameworkFromFlag) { + prompts.push({ + type: "list", + name: "framework", + message: "Pick a framework to create the component for", + choices: frameworks, + }); + } + return inquirer + .prompt(prompts) + .then((answers) => { + const folder = answers.folder || folderFromFlag; + const framework = answers.framework || capitalizeFirstLetter(frameworkFromFlag); + const componentName = answers.componentName || componentNameFromFlag; + switch (framework) { + case "Vue": + return vueWizard(componentName, folder); + case "Angular": + return angularWizard(componentName, folder); + case "React": + return reactWizard(componentName, folder); + case "Svelte": + return svelteWizard(componentName, folder); + case "Qwik": + return qwikWizard(componentName, folder); + case "Astro": + return astroWizard(componentName, folder); + default: + throw new Error("A valid framework must be selected"); + } + }) + .then((values) => { + return values; + }) + .catch((e) => { + throw new Error(e.message); + }); +}; +export default wizard;