├── .prettierignore ├── templates ├── widget-with-ui │ ├── ui-src │ │ ├── App.css │ │ ├── vite-env.d.ts │ │ ├── main.tsx │ │ ├── index.html │ │ ├── index.css │ │ ├── tsconfig.json │ │ └── App.tsx │ ├── manifest.json │ ├── widget-src │ │ ├── tsconfig.json │ │ └── code.tsx │ ├── vite.config.ts │ ├── package.json │ └── README.md └── widget-without-ui │ ├── manifest.json │ ├── widget-src │ ├── tsconfig.json │ └── code.tsx │ ├── package.json │ └── README.md ├── .gitignore ├── .github └── workflows │ └── main.yml ├── test-usage.sh ├── package.json ├── cli.js ├── README.md └── create-widget.js /.prettierignore: -------------------------------------------------------------------------------- 1 | *.json 2 | -------------------------------------------------------------------------------- /templates/widget-with-ui/ui-src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | padding: 20px; 3 | } 4 | -------------------------------------------------------------------------------- /templates/widget-with-ui/ui-src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /templates/widget-with-ui/ui-src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./index.css"; 4 | import App from "./App"; 5 | 6 | ReactDOM.render(, document.getElementById("root")); 7 | -------------------------------------------------------------------------------- /templates/widget-without-ui/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{widgetName}}", 3 | "id": "{{widgetId}}", 4 | "api": "1.0.0", 5 | "editorType": [{{{widgetEditorType}}}], 6 | "permissions": [], 7 | "containsWidget": true, 8 | "main": "dist/code.js", 9 | "widgetApi": "1.0.0" 10 | } 11 | -------------------------------------------------------------------------------- /templates/widget-with-ui/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{widgetName}}", 3 | "id": "{{widgetId}}", 4 | "api": "1.0.0", 5 | "editorType": [{{{widgetEditorType}}}], 6 | "permissions": [], 7 | "containsWidget": true, 8 | "main": "dist/code.js", 9 | "ui": "dist/index.html", 10 | "widgetApi": "1.0.0" 11 | } 12 | -------------------------------------------------------------------------------- /templates/widget-with-ui/widget-src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "react", 4 | "jsxFactory": "figma.widget.h", 5 | "jsxFragmentFactory": "figma.widget.Fragment", 6 | "target": "es6", 7 | "lib": ["es6"], 8 | "strict": true, 9 | "typeRoots": ["../node_modules/@figma"] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .git 2 | 3 | # Node 4 | *.log 5 | *.log.* 6 | node_modules 7 | 8 | # JetBrains IDE 9 | .idea 10 | scopes 11 | 12 | # Sublime IDE 13 | *.sublime-project 14 | *.sublime-workspace 15 | 16 | # vscode IDE 17 | .vscode 18 | 19 | # OSX 20 | .DS_Store 21 | 22 | test/ 23 | test-artifacts/ 24 | templates/*/package-lock.json 25 | 26 | -------------------------------------------------------------------------------- /templates/widget-without-ui/widget-src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "react", 4 | "jsxFactory": "figma.widget.h", 5 | "jsxFragmentFactory": "figma.widget.Fragment", 6 | "target": "es6", 7 | "lib": ["es6"], 8 | "strict": true, 9 | "typeRoots": ["../node_modules/@figma"] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /templates/widget-with-ui/ui-src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Widget Template 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /templates/widget-with-ui/ui-src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /templates/widget-with-ui/ui-src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /templates/widget-with-ui/ui-src/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import "./App.css"; 3 | 4 | function App() { 5 | useEffect(() => { 6 | if (typeof parent !== undefined) { 7 | parent?.postMessage?.({ pluginMessage: "hello" }, "*"); 8 | } 9 | }, []); 10 | 11 | return ( 12 |
13 |

Hello

14 | 21 |
22 | ); 23 | } 24 | 25 | export default App; 26 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | workflow_dispatch: 10 | 11 | jobs: 12 | basic-tests: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Setup Node.js environment 17 | uses: actions/setup-node@v2.1.4 18 | - uses: bahmutov/npm-install@v1 19 | - name: Install Dependencies 20 | if: steps.cache.outputs.cache-hit != 'true' 21 | run: npm ci 22 | - name: prettier 23 | run: npm run format:check 24 | - run: ./test-usage.sh 25 | -------------------------------------------------------------------------------- /templates/widget-without-ui/widget-src/code.tsx: -------------------------------------------------------------------------------- 1 | const { widget } = figma; 2 | const { AutoLayout, Ellipse, Frame, Image, Rectangle, SVG, Text } = widget; 3 | 4 | function Widget() { 5 | return ( 6 | 16 | 17 | Hello Widgets 18 | 19 | 20 | ); 21 | } 22 | widget.register(Widget); 23 | -------------------------------------------------------------------------------- /test-usage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -xueo pipefail 4 | 5 | rm -rf test-artifacts 6 | mkdir -p test-artifacts 7 | pushd test-artifacts 8 | 9 | # Without iframe 10 | node ../cli -n CounterNoUI -p counter-no-ui-widget --iframe=N --editortype --editor-type figma,figjam 11 | pushd counter-no-ui-widget 12 | 13 | if [ -d "ui-src/" ]; then 14 | echo "ERROR: Should not have ui-src folder in no-ui test case" 15 | exit 1 16 | fi 17 | 18 | npm run test 19 | popd 20 | 21 | # With iframe 22 | node ../cli -n CounterWithUI -p counter-with-ui-widget --iframe=Y --editor-type figjam 23 | pushd counter-with-ui-widget 24 | npm run test 25 | popd 26 | 27 | popd 28 | -------------------------------------------------------------------------------- /templates/widget-with-ui/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import reactRefresh from "@vitejs/plugin-react-refresh"; 3 | import { viteSingleFile } from "vite-plugin-singlefile"; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | root: "./ui-src", 8 | plugins: [reactRefresh(), viteSingleFile()], 9 | build: { 10 | target: "esnext", 11 | assetsInlineLimit: 100000000, 12 | chunkSizeWarningLimit: 100000000, 13 | cssCodeSplit: false, 14 | brotliSize: false, 15 | outDir: "../dist", 16 | rollupOptions: { 17 | inlineDynamicImports: true, 18 | output: { 19 | manualChunks: () => "everything.js", 20 | }, 21 | }, 22 | }, 23 | }); 24 | -------------------------------------------------------------------------------- /templates/widget-without-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{packageName}}", 3 | "version": "1.0.0", 4 | "description": "{{widgetName}}", 5 | "scripts": { 6 | "test": "npm run tsc && npm run build", 7 | "format": "prettier --write .", 8 | "tsc": "tsc --noEmit -p widget-src", 9 | "build": "npm run bundle -- --minify", 10 | "bundle": "esbuild widget-src/code.tsx --bundle --outfile=dist/code.js", 11 | "dev": "concurrently -n tsc,build 'npm run tsc -- --preserveWatchOutput --watch' 'npm run bundle -- --watch'" 12 | }, 13 | "author": "Figma", 14 | "license": "MIT License", 15 | "dependencies": {}, 16 | "devDependencies": { 17 | "@figma/plugin-typings": "*", 18 | "@figma/widget-typings": "*", 19 | "concurrently": "^6.3.0", 20 | "esbuild": "^0.13.5", 21 | "prettier": "^2.3.2", 22 | "typescript": "^4.4.2" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@figma/create-widget", 3 | "repository": "git@github.com:figma/create-widget.git", 4 | "version": "1.0.7", 5 | "description": "Create a widget from figma widget templates", 6 | "type": "module", 7 | "bin": { 8 | "create-widget": "./cli.js" 9 | }, 10 | "scripts": { 11 | "format": "prettier --write .", 12 | "format:check": "prettier --check .", 13 | "test": "rm -rf test-artifacts/ && mkdir -p test-artifacts && cd test-artifacts && node ../cli.js" 14 | }, 15 | "author": "Figma", 16 | "license": "MIT License", 17 | "dependencies": { 18 | "fs-extra": "^10.0.0", 19 | "package-json": "^7.0.0", 20 | "url": "^0.11.0", 21 | "inquirer": "^8.2.0", 22 | "sade": "^1.7.4", 23 | "globby": "^12.0.2", 24 | "is-utf8": "^0.2.1", 25 | "mustache": "^4.2.0" 26 | }, 27 | "devDependencies": { 28 | "prettier": "^2.5.1" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import sade from "sade"; 3 | 4 | import { createWidget } from "./create-widget.js"; 5 | 6 | const description = ` 7 | Create a FigJam widget with a single command 8 | 9 | Examples 10 | $ npm init @figma/widget 11 | $ npm init @figma/widget -n Counter 12 | $ npm init @figma/widget -n Counter -p counter-widget --iframe=Y --editor-type figma,figjam 13 | `; 14 | 15 | sade("create-widget", true) 16 | .describe(description) 17 | .option("-n, --name", 'Name of your widget; defaults to "Widget"') 18 | .option( 19 | "-p, --package-name", 20 | 'Name of the folder containing your widget; defaults to "-widget"' 21 | ) 22 | .option( 23 | "-e, --editor-type", 24 | 'Editor type of widget; enter [figma | figjam | figma,figjam]; defaults to "figjam"' 25 | ) 26 | .option("-i, --iframe", "Whether the widget uses an iframe") 27 | .action(async function (options) { 28 | await createWidget({ options }); 29 | }) 30 | .parse(process.argv); 31 | -------------------------------------------------------------------------------- /templates/widget-with-ui/widget-src/code.tsx: -------------------------------------------------------------------------------- 1 | const { widget } = figma; 2 | const { AutoLayout, Ellipse, Frame, Image, Rectangle, SVG, Text } = widget; 3 | 4 | function Widget() { 5 | return ( 6 | { 16 | await new Promise((resolve) => { 17 | figma.showUI(__html__); 18 | figma.ui.on("message", (msg) => { 19 | if (msg === "hello") { 20 | figma.notify("Hello Widgets"); 21 | } 22 | if (msg === "close") { 23 | figma.closePlugin(); 24 | } 25 | }); 26 | }); 27 | }} 28 | > 29 | 30 | Click Me 31 | 32 | 33 | ); 34 | } 35 | widget.register(Widget); 36 | -------------------------------------------------------------------------------- /templates/widget-without-ui/README.md: -------------------------------------------------------------------------------- 1 | # @figma/create-widget 2 | 3 | This repo was created by @figma/create-widget 4 | 5 | ## Getting started with widget development 6 | 7 | Run the following command to start building your widget 8 | 9 | ```bash 10 | npm run dev 11 | ``` 12 | 13 | 1. Log in to your account and open the Figma desktop app 14 | 2. You can open any existing FigJam document or create a new one. 15 | 3. Go to Menu > Widgets > Development > "Import widget from manifest..." 16 | 4. Select the manifest.json in this folder 17 | 18 | ## Organization 19 | 20 | This widget uses: 21 | 22 | - [esbuild](https://esbuild.github.io/) for bundling 23 | - [typescript](https://www.typescriptlang.org/) for typechecking 24 | 25 | | file/folder | description | 26 | | ------------- | -------------------------------------------------------------------------------- | 27 | | manifest.json | The widget's [manifest.json](https://www.figma.com/widget-docs/widget-manifest/) | 28 | | widget-src/ | Contains the widget code | 29 | 30 | ### `npm run dev` 31 | 32 | This is the only command you need to run in development. It will start the following processes for you: 33 | 34 | - bundling 35 | - typechecking 36 | 37 | ### `npm run build` 38 | 39 | This runs bundling with minification turned on. You should run this command before releasing your widget. 40 | 41 | ### `npm run test` 42 | 43 | This runs typechecking and makes sure that your widget builds without errors. 44 | -------------------------------------------------------------------------------- /templates/widget-with-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{packageName}}", 3 | "version": "1.0.0", 4 | "description": "{{widgetName}}", 5 | "scripts": { 6 | "test": "npm run tsc && npm run build", 7 | "format": "prettier --write .", 8 | "tsc": "npm run tsc:main && npm run tsc:ui", 9 | "tsc:main": "tsc --noEmit -p widget-src", 10 | "tsc:ui": "tsc --noEmit -p ui-src", 11 | "tsc:watch": "concurrently -n widget,iframe \"npm run tsc:main -- --watch --preserveWatchOutput\" \"npm run tsc:ui -- --watch --preserveWatchOutput\"", 12 | "build": "npm run build:ui && npm run build:main -- --minify", 13 | "build:main": "esbuild widget-src/code.tsx --bundle --outfile=dist/code.js", 14 | "build:ui": "npx vite build --minify esbuild --emptyOutDir=false", 15 | "build:watch": "concurrently -n widget,iframe \"npm run build:main -- --watch\" \"npm run build:ui -- --watch\"", 16 | "dev": "concurrently -n tsc,build,vite 'npm:tsc:watch' 'npm:build:watch' 'vite'" 17 | }, 18 | "author": "Figma", 19 | "license": "MIT License", 20 | "dependencies": { 21 | "react": "^17.0.0", 22 | "react-dom": "^17.0.0" 23 | }, 24 | "devDependencies": { 25 | "@figma/plugin-typings": "*", 26 | "@figma/widget-typings": "*", 27 | "@types/react": "^17.0.0", 28 | "@types/react-dom": "^17.0.0", 29 | "@vitejs/plugin-react-refresh": "^1.3.1", 30 | "concurrently": "^6.3.0", 31 | "esbuild": "^0.13.5", 32 | "prettier": "^2.3.2", 33 | "typescript": "^4.4.2", 34 | "vite": "^2.5.2", 35 | "vite-plugin-singlefile": "^0.5.1" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /templates/widget-with-ui/README.md: -------------------------------------------------------------------------------- 1 | # @figma/create-widget 2 | 3 | This repo was created by @figma/create-widget 4 | 5 | ## Getting started 6 | 7 | Run the following command to start building your widget 8 | 9 | ```bash 10 | npm run dev 11 | ``` 12 | 13 | 1. Log in to your account and open the Figma desktop app 14 | 2. You can open any existing FigJam document or create a new one. 15 | 3. Go to Menu > Widgets > Development > "Import widget from manifest..." 16 | 4. Select the manifest.json in this folder 17 | 18 | ## Organization 19 | 20 | This widget uses: 21 | 22 | - [esbuild](https://esbuild.github.io/) for bundling 23 | - [vite](https://vitejs.dev/) and [react](https://reactjs.org/) for the iframe 24 | - [typescript](https://www.typescriptlang.org/) for typechecking 25 | 26 | | file/folder | description | 27 | | ------------- | -------------------------------------------------------------------------------- | 28 | | manifest.json | The widget's [manifest.json](https://www.figma.com/widget-docs/widget-manifest/) | 29 | | widget-src/ | Contains the widget code | 30 | | ui-src/ | Contains the iframe code | 31 | 32 | ### `npm run dev` 33 | 34 | This is the only command you need to run in development. It will start the following processes for you: 35 | 36 | - bundling (both widget and iframe code) 37 | - typechecking (both widget and iframe code) 38 | - vite dev server (for iframe development) 39 | 40 | ### `npm run build` 41 | 42 | This runs bundling with minification turned on. You should run this command before releasing your widget. 43 | 44 | ### `npm run test` 45 | 46 | This runs typechecking and makes sure that your widget builds without errors. 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @figma/create-widget 2 | 3 | [![npm](https://img.shields.io/npm/v/@figma/create-widget?logo=npm&cacheSeconds=1800)](https://www.npmjs.com/package/@figma/create-widget) 4 | 5 | Create new [Figma & FigJam widgets](https://figma.com/widget-docs) with a single command. 6 | 7 | ```bash 8 | npm init @figma/widget 9 | ``` 10 | 11 | ## Widget Organization 12 | 13 | The created widgets use: 14 | 15 | - [esbuild](https://esbuild.github.io/) for bundling 16 | - [vite](https://vitejs.dev/) and [react](https://reactjs.org/) if the iframe option is specified 17 | - [typescript](https://www.typescriptlang.org/) for typechecking 18 | 19 | | file/folder | description | 20 | | ------------- | -------------------------------------------------------------------------------- | 21 | | manifest.json | The widget's [manifest.json](https://www.figma.com/widget-docs/widget-manifest/) | 22 | | widget-src/ | Contains the widget code | 23 | | ui-src/ | Contains the iframe code | 24 | 25 | ## Getting Started 26 | 27 | After running `npm init @figma/widget`, follow the provided prompts. 28 | 29 | ### `npm run dev` 30 | 31 | This is the only command you need to run in development. It will start the following processes for you: 32 | 33 | - bundling (both widget and iframe code) 34 | - typechecking (both widget and iframe code) 35 | - vite dev server (for iframe development) 36 | 37 | ### `npm run build` 38 | 39 | This runs bundling with minification turned on. You should run this command before releasing your widget. 40 | 41 | ### `npm run test` 42 | 43 | This runs typechecking and makes sure that your widget builds without errors. 44 | 45 | ## Credit 46 | 47 | Credit to https://github.com/yuanqing/create-figma-plugin for providing a way to `npm init` widgets and plugins before this repository ever existed. 48 | -------------------------------------------------------------------------------- /create-widget.js: -------------------------------------------------------------------------------- 1 | import fs from "fs-extra"; 2 | import path from "path"; 3 | import cp from "child_process"; 4 | 5 | import { fileURLToPath } from "url"; 6 | import inquirer from "inquirer"; 7 | import { globby } from "globby"; 8 | import isUtf8 from "is-utf8"; 9 | import mustache from "mustache"; 10 | 11 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); 12 | 13 | const INITIAL_PROMPT = `This tool will create a FigJam widget project using a template that serves as a starting point for building your widget. 14 | 15 | See the generated README.md for more information on how to use this template and get started building your widget. 16 | 17 | You can find the API reference for widgets here: https://www.figma.com/widget-docs/api/api-reference/ 18 | 19 | Press ^C at any time to quit.\n`; 20 | 21 | function makeid(length) { 22 | var result = ""; 23 | var characters = 24 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 25 | var charactersLength = characters.length; 26 | for (var i = 0; i < length; i++) { 27 | result += characters.charAt(Math.floor(Math.random() * charactersLength)); 28 | } 29 | return result; 30 | } 31 | 32 | function randomWidgetId() { 33 | return "widget-id-" + makeid(10); 34 | } 35 | 36 | export async function replaceTemplatizedValues(directory, values) { 37 | const filePaths = await globby("**/*", { 38 | cwd: directory, 39 | dot: true, 40 | }); 41 | await Promise.all( 42 | filePaths.map(async function (filePath) { 43 | const absolutePath = path.join(directory, filePath); 44 | const buffer = await fs.readFile(absolutePath); 45 | const fileContents = isUtf8(buffer) 46 | ? mustache.render(buffer.toString(), values) 47 | : buffer; 48 | await fs.outputFile(absolutePath, fileContents); 49 | }) 50 | ); 51 | } 52 | 53 | async function installDependencies(cwd, widgetname, destinationPath) { 54 | await new (function (resolve, reject) { 55 | const command = "npm install"; 56 | cp.exec(command, { cwd }, function (error) { 57 | if (error) { 58 | reject(error); 59 | return; 60 | } 61 | path.resolve(); 62 | 63 | console.log(); 64 | console.log(` 65 | Your widget has been created! 66 | 67 | 68 | Run the following commands to get started building your widget: 69 | 70 | cd ${destinationPath} 71 | npm run dev 72 | 73 | 74 | Import your widget into FigJam to start testing it: 75 | 76 | 1. Open a FigJam file using the Figma Desktop app to import your widget. 77 | 2. Right click > Widgets > Development > Import widget from manifest… and import the manifest.json file in your widget directory. 78 | 3. Insert your your widget: "Right click > Widgets > Development > ${widgetname}" 79 | `); 80 | }); 81 | })(); 82 | } 83 | 84 | async function copyTemplateFiles(pluginDirectoryPath, shouldAddUI) { 85 | const templateName = shouldAddUI ? "widget-with-ui" : "widget-without-ui"; 86 | const templateDirectory = path.resolve( 87 | __dirname, 88 | "..", 89 | "create-widget", 90 | "templates", 91 | templateName 92 | ); 93 | await fs.copy(templateDirectory, pluginDirectoryPath); 94 | } 95 | 96 | export async function createWidget(input) { 97 | try { 98 | console.log(INITIAL_PROMPT); 99 | let widgetName = input.options.name; 100 | if (widgetName === undefined) { 101 | const result = await inquirer.prompt([ 102 | { 103 | message: 104 | 'Enter the name of your widget: (empty defaults to "Widget")', 105 | name: "widgetName", 106 | type: "input", 107 | }, 108 | ]); 109 | widgetName = result.widgetName ? result.widgetName : "Widget"; 110 | } 111 | 112 | let destinationPath = input.options["package-name"]; 113 | if (destinationPath === undefined) { 114 | const defaultDestinationPath = `${widgetName.toLowerCase()}-widget`; 115 | const result = await inquirer.prompt([ 116 | { 117 | message: `Enter the folder name for your widget: (empty defaults to "${defaultDestinationPath}")`, 118 | name: "destinationPath", 119 | type: "input", 120 | }, 121 | ]); 122 | destinationPath = result.destinationPath 123 | ? result.destinationPath 124 | : defaultDestinationPath; 125 | } 126 | 127 | let directoryPath = path.join(process.cwd(), destinationPath); 128 | while ((await fs.pathExists(directoryPath)) === true) { 129 | throw new Error( 130 | `${destinationPath} already exists. Please choose a different destination folder name.` 131 | ); 132 | } 133 | 134 | let rawEditorType = input.options["editor-type"]; 135 | let editorType; 136 | if (rawEditorType === undefined) { 137 | const result = await inquirer.prompt([ 138 | { 139 | choices: ["figma", "figjam", "figma,figjam"], 140 | message: `Select the editor type(s) name for your widget:")`, 141 | name: "editorType", 142 | type: "list", 143 | }, 144 | ]); 145 | rawEditorType = result.editorType || "figjam"; 146 | editorType = rawEditorType 147 | .split(",") 148 | .map((e) => `\"${e}\"`) 149 | .join(","); 150 | console.log(editorType); 151 | } 152 | 153 | let shouldAddUI = input.options.iframe; 154 | if (shouldAddUI === undefined) { 155 | const result = await inquirer.prompt([ 156 | { 157 | choices: ["Y", "N"], 158 | message: "Are you building a widget with an iframe?", 159 | name: "shouldAddIframe", 160 | type: "list", 161 | }, 162 | ]); 163 | shouldAddUI = result.shouldAddIframe; 164 | } 165 | shouldAddUI = shouldAddUI === "Y"; 166 | 167 | console.log(``); 168 | console.log( 169 | `Creating widget for ${rawEditorType} ${ 170 | shouldAddUI ? "with ui" : "without ui" 171 | }...` 172 | ); 173 | console.log(`Copying template into "${destinationPath}"...`); 174 | 175 | await copyTemplateFiles(directoryPath, shouldAddUI); 176 | await replaceTemplatizedValues(directoryPath, { 177 | widgetName, 178 | widgetId: randomWidgetId(), 179 | widgetEditorType: editorType, 180 | packageName: widgetName.toLowerCase(), 181 | }); 182 | 183 | console.log("Installing dependencies..."); 184 | await installDependencies(directoryPath, widgetName, destinationPath); 185 | } catch (error) { 186 | console.log(error.message); 187 | process.exit(1); 188 | } 189 | } 190 | --------------------------------------------------------------------------------