diff --git a/package-lock.json b/package-lock.json index 511f2433..f7294296 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,15 @@ { "name": "@powerhousedao/connect", - "version": "1.0.0-dev.145", + "version": "1.0.0-dev.146", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@powerhousedao/connect", - "version": "1.0.0-dev.145", + "version": "1.0.0-dev.146", "license": "AGPL-3.0-only", "dependencies": { + "commander": "^12.1.0", "vite": "^5.4.8", "vite-envs": "^4.4.5" }, @@ -2787,6 +2788,15 @@ "node": ">= 14.17.5" } }, + "node_modules/@electron-forge/cli/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/@electron-forge/core": { "version": "6.4.2", "resolved": "https://registry.npmjs.org/@electron-forge/core/-/core-6.4.2.tgz", @@ -15133,12 +15143,11 @@ "peer": true }, "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "engines": { - "node": ">= 6" + "node": ">=18" } }, "node_modules/common-tags": { @@ -22797,15 +22806,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lint-staged/node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dev": true, - "engines": { - "node": ">=18" - } - }, "node_modules/lint-staged/node_modules/emoji-regex": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", @@ -33315,6 +33315,15 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/sucrase/node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", diff --git a/package.json b/package.json index 7723a37c..efcb4e69 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "build:service-worker": "tsc --build ./tsconfig.sw.json" }, "dependencies": { + "commander": "^12.1.0", "vite": "^5.4.8", "vite-envs": "^4.4.5" }, diff --git a/studio/cli.js b/studio/cli.js index b2ee75e7..b61f663a 100755 --- a/studio/cli.js +++ b/studio/cli.js @@ -1,7 +1,5 @@ #!/usr/bin/env node -import { startServer } from './index.js'; +import { program } from './index.js'; -startServer().catch(error => { - throw error; -}); +program.parse(process.argv); diff --git a/studio/index.ts b/studio/index.ts index f87ea483..d2fb5e9c 100644 --- a/studio/index.ts +++ b/studio/index.ts @@ -1,89 +1,102 @@ -import { exec } from 'node:child_process'; +import { Command } from 'commander'; import fs from 'node:fs'; -import { join } from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { createLogger, createServer, InlineConfig, Plugin } from 'vite'; -import { viteEnvs } from 'vite-envs'; -import { getStudioConfig, viteConnectDevStudioPlugin } from './vite-plugin'; - -const studioDirname = fileURLToPath(new URL('.', import.meta.url)); -const appPath = join(studioDirname, '..'); -const viteEnvsScript = join(appPath, 'vite-envs.sh'); - -// silences dynamic import warnings -const logger = createLogger(); -// eslint-disable-next-line @typescript-eslint/unbound-method -const loggerWarn = logger.warn; -/** - * @param {string} msg - * @param {import('vite').LogOptions} options - */ -logger.warn = (msg, options) => { - if (msg.includes('The above dynamic import cannot be analyzed by Vite.')) { - return; +import { dirname, isAbsolute, join, resolve } from 'node:path'; + +import { startServer } from './server'; + +type PowerhouseConfig = { + documentModelsDir?: string; + editorsDir?: string; +}; + +const readJsonFile = (filePath: string): PowerhouseConfig | null => { + try { + const absolutePath = resolve(filePath); + const fileContents = fs.readFileSync(absolutePath, 'utf-8'); + return JSON.parse(fileContents) as PowerhouseConfig; + } catch (error) { + console.error(`Error reading file: ${filePath}`); + return null; } - loggerWarn(msg, options); }; -function runShellScriptPlugin(scriptPath: string): Plugin { - return { - name: 'vite-plugin-run-shell-script', - buildStart() { - if (fs.existsSync(scriptPath)) { - exec(`sh ${scriptPath}`, (error, stdout, stderr) => { - if (error) { - console.error( - `Error executing the script: ${error.message}`, - ); - return; - } - if (stderr) { - console.error(stderr); - } - }); - } - }, - }; -} - -export async function startServer() { - const PORT = process.env.PORT ? parseInt(process.env.PORT) : 3000; - const studioConfig = getStudioConfig(); - - // needed for viteEnvs - if (!fs.existsSync(join(appPath, 'src'))) { - fs.mkdirSync(join(appPath, 'src')); +type ConnectStudioOptions = { + port?: string; + host?: boolean; + configFile?: string; + localEditors?: string; + localDocuments?: string; +}; + +type ConnectStudioAction = (options: ConnectStudioOptions) => void; + +const connectStudioAction: ConnectStudioAction = options => { + if (options.port) { + process.env.PORT = options.port; + } + + if (options.host) { + process.env.HOST = options.host.toString(); + } + + if (options.configFile) { + const config = readJsonFile(options.configFile); + if (!config) return; + + const configFileDir = dirname(options.configFile); + + if (config.documentModelsDir) { + process.env.LOCAL_DOCUMENT_MODELS = isAbsolute( + config.documentModelsDir, + ) + ? config.documentModelsDir + : join(configFileDir, config.documentModelsDir); + } + + if (config.editorsDir) { + process.env.LOCAL_DOCUMENT_EDITORS = isAbsolute(config.editorsDir) + ? config.editorsDir + : join(configFileDir, config.editorsDir); + } } - process.env.PH_CONNECT_STUDIO_MODE = 'true'; - - const config: InlineConfig = { - customLogger: logger, - configFile: false, - root: appPath, - server: { - port: PORT, - open: true, - }, - plugins: [ - viteConnectDevStudioPlugin(true), - viteEnvs({ - declarationFile: join(studioDirname, '../.env'), - computedEnv: studioConfig, - }), - runShellScriptPlugin(viteEnvsScript), - ], - build: { - rollupOptions: { - input: 'index.html', - }, - }, - }; - - const server = await createServer(config); - - await server.listen(); - - server.printUrls(); - server.bindCLIShortcuts({ print: true }); -} + if (options.localEditors) { + process.env.LOCAL_DOCUMENT_EDITORS = options.localEditors; + } + + if (options.localDocuments) { + process.env.LOCAL_DOCUMENT_MODELS = options.localDocuments; + } + + startServer().catch(error => { + throw error; + }); +}; + +export const program = new Command(); + +program + .name('Connect Studio') + .description('Connect Studio CLI') + .option('-p, --port ', 'Port to run the server on', '3000') + .option('-h, --host', 'Expose the server to the network') + .option( + '--config-file ', + 'Path to the powerhouse.config.js file', + ) + .option( + '-le, --local-editors ', + 'Link local document editors path', + ) + .option( + '-ld, --local-documents ', + 'Link local documents path', + ) + .action(connectStudioAction); + +program + .command('help') + .description('Display help information') + .action(() => { + program.help(); + }); diff --git a/studio/server.ts b/studio/server.ts new file mode 100644 index 00000000..5803d617 --- /dev/null +++ b/studio/server.ts @@ -0,0 +1,90 @@ +import { exec } from 'node:child_process'; +import fs from 'node:fs'; +import { join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { createLogger, createServer, InlineConfig, Plugin } from 'vite'; +import { viteEnvs } from 'vite-envs'; +import { getStudioConfig, viteConnectDevStudioPlugin } from './vite-plugin'; + +const studioDirname = fileURLToPath(new URL('.', import.meta.url)); +const appPath = join(studioDirname, '..'); +const viteEnvsScript = join(appPath, 'vite-envs.sh'); + +// silences dynamic import warnings +const logger = createLogger(); +// eslint-disable-next-line @typescript-eslint/unbound-method +const loggerWarn = logger.warn; +/** + * @param {string} msg + * @param {import('vite').LogOptions} options + */ +logger.warn = (msg, options) => { + if (msg.includes('The above dynamic import cannot be analyzed by Vite.')) { + return; + } + loggerWarn(msg, options); +}; + +function runShellScriptPlugin(scriptPath: string): Plugin { + return { + name: 'vite-plugin-run-shell-script', + buildStart() { + if (fs.existsSync(scriptPath)) { + exec(`sh ${scriptPath}`, (error, stdout, stderr) => { + if (error) { + console.error( + `Error executing the script: ${error.message}`, + ); + return; + } + if (stderr) { + console.error(stderr); + } + }); + } + }, + }; +} + +export async function startServer() { + const PORT = process.env.PORT ? parseInt(process.env.PORT) : 3000; + const studioConfig = getStudioConfig(); + + // needed for viteEnvs + if (!fs.existsSync(join(appPath, 'src'))) { + fs.mkdirSync(join(appPath, 'src')); + } + + process.env.PH_CONNECT_STUDIO_MODE = 'true'; + + const config: InlineConfig = { + customLogger: logger, + configFile: false, + root: appPath, + server: { + port: PORT, + open: true, + host: Boolean(process.env.HOST), + }, + plugins: [ + viteConnectDevStudioPlugin(true), + viteEnvs({ + declarationFile: join(studioDirname, '../.env'), + computedEnv: studioConfig, + }), + runShellScriptPlugin(viteEnvsScript), + ], + build: { + rollupOptions: { + input: 'index.html', + }, + }, + }; + + const server = await createServer(config); + + await server.listen(); + + server.printUrls(); + server.bindCLIShortcuts({ print: true }); +}