2023-03-02 19:21:45 +01:00
|
|
|
import { Command } from "../structures/Command"
|
2023-11-20 16:19:31 +01:00
|
|
|
import {
|
|
|
|
CommandWord,
|
|
|
|
AnalyticsEvent,
|
|
|
|
InitType,
|
|
|
|
GENERATED_USER_EMAIL,
|
|
|
|
} from "../constants"
|
2023-03-02 19:21:45 +01:00
|
|
|
import { getSkeleton, fleshOutSkeleton } from "./skeleton"
|
|
|
|
import * as questions from "../questions"
|
|
|
|
import fs from "fs"
|
|
|
|
import { PluginType, PLUGIN_TYPE_ARR } from "@budibase/types"
|
|
|
|
import { plugins } from "@budibase/backend-core"
|
|
|
|
import { runPkgCommand } from "../exec"
|
|
|
|
import { join } from "path"
|
|
|
|
import { success, error, info, moveDirectory } from "../utils"
|
|
|
|
import { captureEvent } from "../events"
|
|
|
|
import { init as hostingInit } from "../hosting/init"
|
|
|
|
import { start as hostingStart } from "../hosting/start"
|
2023-11-20 21:52:29 +01:00
|
|
|
|
2022-09-29 16:38:54 +02:00
|
|
|
const fp = require("find-free-port")
|
2023-03-02 19:21:45 +01:00
|
|
|
|
|
|
|
type PluginOpts = {
|
|
|
|
init?: PluginType
|
|
|
|
}
|
2022-08-10 14:11:57 +02:00
|
|
|
|
2022-08-18 14:29:49 +02:00
|
|
|
function checkInPlugin() {
|
|
|
|
if (!fs.existsSync("package.json")) {
|
|
|
|
throw new Error(
|
|
|
|
"Please run in a plugin directory - must contain package.json"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
if (!fs.existsSync("schema.json")) {
|
|
|
|
throw new Error(
|
|
|
|
"Please run in a plugin directory - must contain schema.json"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-02 19:21:45 +01:00
|
|
|
async function askAboutTopLevel(name: string) {
|
2022-09-13 19:28:03 +02:00
|
|
|
const files = fs.readdirSync(process.cwd())
|
|
|
|
// we are in an empty git repo, don't ask
|
2022-09-14 12:11:20 +02:00
|
|
|
if (files.find(file => file === ".git")) {
|
2022-09-13 19:28:03 +02:00
|
|
|
return false
|
|
|
|
} else {
|
|
|
|
console.log(
|
|
|
|
info(`By default the plugin will be created in the directory "${name}"`)
|
|
|
|
)
|
|
|
|
console.log(
|
|
|
|
info(
|
|
|
|
"if you are already in an empty directory, such as a new Git repo, you can disable this functionality."
|
|
|
|
)
|
|
|
|
)
|
|
|
|
return questions.confirmation("Create top level directory?")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-22 18:24:00 +02:00
|
|
|
async function init(opts: PluginOpts | PluginType) {
|
|
|
|
const type = (opts as PluginOpts).init || (opts as PluginType)
|
2022-09-09 12:25:17 +02:00
|
|
|
if (!type || !PLUGIN_TYPE_ARR.includes(type)) {
|
2022-08-10 17:19:08 +02:00
|
|
|
console.log(
|
|
|
|
error(
|
2023-04-10 23:33:53 +02:00
|
|
|
"Please provide a type to init, either 'component', 'datasource' or 'automation'."
|
2022-08-10 17:19:08 +02:00
|
|
|
)
|
2022-08-10 14:11:57 +02:00
|
|
|
)
|
|
|
|
return
|
|
|
|
}
|
2022-08-10 17:19:08 +02:00
|
|
|
console.log(info("Lets get some details about your new plugin:"))
|
2022-08-10 14:11:57 +02:00
|
|
|
const name = await questions.string("Name", `budibase-${type}`)
|
|
|
|
if (fs.existsSync(name)) {
|
2022-08-10 17:19:08 +02:00
|
|
|
console.log(
|
|
|
|
error("Directory by plugin name already exists, pick a new name.")
|
|
|
|
)
|
2022-08-10 14:11:57 +02:00
|
|
|
return
|
|
|
|
}
|
2022-09-14 18:35:46 +02:00
|
|
|
const description = await questions.string(
|
2022-08-10 14:11:57 +02:00
|
|
|
"Description",
|
|
|
|
`An amazing Budibase ${type}!`
|
|
|
|
)
|
|
|
|
const version = await questions.string("Version", "1.0.0")
|
2022-09-14 12:04:37 +02:00
|
|
|
const topLevel = await askAboutTopLevel(name)
|
2022-08-10 14:11:57 +02:00
|
|
|
// get the skeleton
|
2022-08-11 12:03:33 +02:00
|
|
|
console.log(info("Retrieving project..."))
|
2022-08-10 14:11:57 +02:00
|
|
|
await getSkeleton(type, name)
|
2022-09-14 18:35:46 +02:00
|
|
|
await fleshOutSkeleton(type, name, description, version)
|
2022-08-11 12:03:33 +02:00
|
|
|
console.log(info("Installing dependencies..."))
|
2022-08-11 18:29:07 +02:00
|
|
|
await runPkgCommand("install", join(process.cwd(), name))
|
2022-09-13 19:22:15 +02:00
|
|
|
// if no parent directory desired move to cwd
|
|
|
|
if (!topLevel) {
|
|
|
|
moveDirectory(name, process.cwd())
|
|
|
|
console.log(info(`Plugin created in current directory.`))
|
|
|
|
} else {
|
|
|
|
console.log(info(`Plugin created in directory "${name}"`))
|
|
|
|
}
|
2023-03-02 19:21:45 +01:00
|
|
|
captureEvent(AnalyticsEvent.PluginInit, {
|
2022-09-14 18:35:46 +02:00
|
|
|
type,
|
|
|
|
name,
|
|
|
|
description,
|
|
|
|
version,
|
|
|
|
})
|
2022-08-10 14:11:57 +02:00
|
|
|
}
|
|
|
|
|
2022-08-16 17:27:03 +02:00
|
|
|
async function verify() {
|
2022-08-18 14:29:49 +02:00
|
|
|
// will throw errors if not acceptable
|
|
|
|
checkInPlugin()
|
2022-08-10 17:19:08 +02:00
|
|
|
console.log(info("Verifying plugin..."))
|
|
|
|
const schema = fs.readFileSync("schema.json", "utf8")
|
|
|
|
const pkg = fs.readFileSync("package.json", "utf8")
|
|
|
|
let name, version
|
|
|
|
try {
|
|
|
|
const schemaJson = JSON.parse(schema)
|
|
|
|
const pkgJson = JSON.parse(pkg)
|
|
|
|
if (!pkgJson.name || !pkgJson.version || !pkgJson.description) {
|
|
|
|
throw new Error(
|
|
|
|
"package.json is missing one of 'name', 'version' or 'description'."
|
|
|
|
)
|
|
|
|
}
|
|
|
|
name = pkgJson.name
|
|
|
|
version = pkgJson.version
|
2022-11-23 19:25:20 +01:00
|
|
|
plugins.validate(schemaJson)
|
2022-08-16 17:27:03 +02:00
|
|
|
return { name, version }
|
2023-03-02 19:21:45 +01:00
|
|
|
} catch (err: any) {
|
2022-08-10 17:19:08 +02:00
|
|
|
if (err && err.message && err.message.includes("not valid JSON")) {
|
|
|
|
console.log(error(`schema.json is not valid JSON: ${err.message}`))
|
|
|
|
} else {
|
|
|
|
console.log(error(`Invalid schema/package.json: ${err.message}`))
|
|
|
|
}
|
2022-08-16 17:27:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function build() {
|
|
|
|
const verified = await verify()
|
2023-03-02 19:21:45 +01:00
|
|
|
if (!verified?.name) {
|
2022-08-10 17:19:08 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
console.log(success("Verified!"))
|
|
|
|
console.log(info("Building plugin..."))
|
|
|
|
await runPkgCommand("build")
|
2022-08-16 17:27:03 +02:00
|
|
|
const output = join("dist", `${verified.name}-${verified.version}.tar.gz`)
|
2022-08-10 17:19:08 +02:00
|
|
|
console.log(success(`Build complete - output in: ${output}`))
|
|
|
|
}
|
2022-08-10 14:11:57 +02:00
|
|
|
|
2022-08-16 17:27:03 +02:00
|
|
|
async function watch() {
|
|
|
|
const verified = await verify()
|
2023-03-02 19:21:45 +01:00
|
|
|
if (!verified?.name) {
|
2022-08-16 17:27:03 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
const output = join("dist", `${verified.name}-${verified.version}.tar.gz`)
|
|
|
|
console.log(info(`Watching - build in: ${output}`))
|
|
|
|
try {
|
|
|
|
await runPkgCommand("watch")
|
|
|
|
} catch (err) {
|
|
|
|
// always errors when user escapes
|
|
|
|
console.log(success("Watch exited."))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-29 16:38:54 +02:00
|
|
|
async function dev() {
|
|
|
|
const pluginDir = await questions.string("Directory to watch", "./")
|
|
|
|
const [port] = await fp(10000)
|
2022-09-30 18:13:50 +02:00
|
|
|
const password = "admin"
|
2022-09-29 16:38:54 +02:00
|
|
|
await hostingInit({
|
2023-03-02 19:21:45 +01:00
|
|
|
init: InitType.QUICK,
|
2022-09-29 16:38:54 +02:00
|
|
|
single: true,
|
|
|
|
watchPluginDir: pluginDir,
|
2022-09-30 18:13:50 +02:00
|
|
|
genUser: password,
|
2022-09-29 16:38:54 +02:00
|
|
|
port,
|
2022-09-30 18:13:50 +02:00
|
|
|
silent: true,
|
2022-09-29 16:38:54 +02:00
|
|
|
})
|
|
|
|
await hostingStart()
|
2022-09-30 18:13:50 +02:00
|
|
|
console.log(success(`Configuration has been written to docker-compose.yaml`))
|
|
|
|
console.log(
|
|
|
|
success("Development environment started successfully - connect at: ") +
|
|
|
|
info(`http://localhost:${port}`)
|
|
|
|
)
|
|
|
|
console.log(success("Use the following credentials to login:"))
|
|
|
|
console.log(success("Email: ") + info(GENERATED_USER_EMAIL))
|
|
|
|
console.log(success("Password: ") + info(password))
|
2022-09-29 16:38:54 +02:00
|
|
|
}
|
|
|
|
|
2023-03-02 19:21:45 +01:00
|
|
|
export default new Command(`${CommandWord.PLUGIN}`)
|
2022-08-10 14:11:57 +02:00
|
|
|
.addHelp(
|
|
|
|
"Custom plugins for Budibase, init, build and verify your components and datasources with this tool."
|
|
|
|
)
|
|
|
|
.addSubOption(
|
|
|
|
"--init [type]",
|
|
|
|
"Init a new plugin project, with a type of either component or datasource.",
|
|
|
|
init
|
|
|
|
)
|
|
|
|
.addSubOption(
|
|
|
|
"--build",
|
|
|
|
"Build your plugin, this will verify and produce a final tarball for your project.",
|
|
|
|
build
|
|
|
|
)
|
2022-08-16 17:27:03 +02:00
|
|
|
.addSubOption(
|
|
|
|
"--watch",
|
|
|
|
"Automatically build any changes to your plugin.",
|
|
|
|
watch
|
|
|
|
)
|
2022-09-29 16:38:54 +02:00
|
|
|
.addSubOption(
|
|
|
|
"--dev",
|
|
|
|
"Run a development environment which automatically watches the current directory.",
|
|
|
|
dev
|
|
|
|
)
|