@fluid-app/fluid-cli-portal 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +14 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +364 -14
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/templates/base/.oxlintrc.json +9 -0
- package/templates/base/src/index.css +1 -156
- package/templates/base/src/main.tsx +3 -40
- package/templates/base/src/navigation.config.ts +5 -0
- package/templates/base/src/portal.config.ts +18 -10
- package/templates/base/src/screens/ExampleForm.tsx +4 -11
- package/templates/fullstack/package.json.template +2 -5
- package/templates/fullstack/vite.config.ts +2 -0
- package/templates/starter/package.json.template +3 -1
- package/templates/starter/vite.config.ts +19 -3
- package/templates/base/src/App.tsx +0 -18
- package/templates/fullstack/eslint.config.js +0 -13
- package/templates/fullstack/src/fluid.config.ts.template +0 -62
- package/templates/starter/src/fluid.config.ts.template +0 -62
package/dist/index.d.mts
CHANGED
|
@@ -106,6 +106,13 @@ interface DestroyOptions {
|
|
|
106
106
|
readonly yes?: boolean;
|
|
107
107
|
}
|
|
108
108
|
/**
|
|
109
|
+
* Options for the widget create command
|
|
110
|
+
*/
|
|
111
|
+
interface WidgetCreateOptions {
|
|
112
|
+
/** Category for palette grouping */
|
|
113
|
+
readonly category?: string;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
109
116
|
* Template variables for Handlebars processing
|
|
110
117
|
*/
|
|
111
118
|
interface TemplateVariables {
|
|
@@ -236,6 +243,12 @@ declare const createCommand: Command;
|
|
|
236
243
|
//#region src/commands/destroy.d.ts
|
|
237
244
|
declare const destroyCommand: Command;
|
|
238
245
|
//#endregion
|
|
246
|
+
//#region src/commands/widget-create.d.ts
|
|
247
|
+
declare const widgetCommand: Command;
|
|
248
|
+
//#endregion
|
|
249
|
+
//#region src/commands/doctor.d.ts
|
|
250
|
+
declare const doctorCommand: Command;
|
|
251
|
+
//#endregion
|
|
239
252
|
//#region src/utils/cloud-run.d.ts
|
|
240
253
|
/**
|
|
241
254
|
* Cloud Run error codes and default messages
|
|
@@ -572,5 +585,5 @@ declare function provisionDatabase(config: TursoConfig, projectName: string, loc
|
|
|
572
585
|
//#region src/index.d.ts
|
|
573
586
|
declare const plugin: FluidPlugin;
|
|
574
587
|
//#endregion
|
|
575
|
-
export { type BuildOptions, CLOUD_RUN_ERRORS, type CloudRunConfig, type CloudRunDeployCallbacks, type CloudRunError, type CloudRunResult, type CreateOptions, type DeployOptions, type DestroyOptions, type DevOptions, FILE_SYSTEM_ERRORS, FLUID_API_ERROR, type FileSystemError, type FileSystemErrorCode, type FluidApiError, type FluidApiErrorCode, type FluidCompany, type ProjectConfig, type ResolvedTursoConfig, type SelectedPageTemplate, TEMPLATES, TURSO_ERROR, type TemplateName, type TemplatePaths, type TemplateVariables, type TursoConfig, type TursoConfigSource, type TursoDatabase, type TursoError, type TursoOrg, type TursoProvisionCallbacks, copyTemplate, copyTemplateSafe, createCommand, createDatabase, createDatabaseToken, createDirectory, createDirectorySafe, plugin as default, deleteCloudRunService, deleteDatabase, deployToCloudRun, destroyCommand, directoryExists, ensureGroup, fetchLocations, fileExists, getGcpProject, getInstallCommand, getRunCommand, getSdkVersion, getSdkVersionSafe, getTemplatePaths, installDependencies, isTemplateName, parseOrgList, pathExists, promptProjectConfig, provisionDatabase, readFileSafe, resolveFluidApiKey, resolveTursoConfig, runPackageManager, validateFluidApiKey, validateGcloudAuth, validateGcloudInstalled, validateLocation, validateTursoConfig, writeFileSafe };
|
|
588
|
+
export { type BuildOptions, CLOUD_RUN_ERRORS, type CloudRunConfig, type CloudRunDeployCallbacks, type CloudRunError, type CloudRunResult, type CreateOptions, type DeployOptions, type DestroyOptions, type DevOptions, FILE_SYSTEM_ERRORS, FLUID_API_ERROR, type FileSystemError, type FileSystemErrorCode, type FluidApiError, type FluidApiErrorCode, type FluidCompany, type ProjectConfig, type ResolvedTursoConfig, type SelectedPageTemplate, TEMPLATES, TURSO_ERROR, type TemplateName, type TemplatePaths, type TemplateVariables, type TursoConfig, type TursoConfigSource, type TursoDatabase, type TursoError, type TursoOrg, type TursoProvisionCallbacks, type WidgetCreateOptions, copyTemplate, copyTemplateSafe, createCommand, createDatabase, createDatabaseToken, createDirectory, createDirectorySafe, plugin as default, deleteCloudRunService, deleteDatabase, deployToCloudRun, destroyCommand, directoryExists, doctorCommand, ensureGroup, fetchLocations, fileExists, getGcpProject, getInstallCommand, getRunCommand, getSdkVersion, getSdkVersionSafe, getTemplatePaths, installDependencies, isTemplateName, parseOrgList, pathExists, promptProjectConfig, provisionDatabase, readFileSafe, resolveFluidApiKey, resolveTursoConfig, runPackageManager, validateFluidApiKey, validateGcloudAuth, validateGcloudInstalled, validateLocation, validateTursoConfig, widgetCommand, writeFileSafe };
|
|
576
589
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/utils/package-manager.ts","../src/utils/file-system.ts","../src/utils/prompts.ts","../src/commands/create.ts","../src/commands/destroy.ts","../src/utils/cloud-run.ts","../src/utils/fluid-api.ts","../src/utils/turso.ts","../src/index.ts"],"mappings":";;;;;;;cAOa,SAAA;EAAA,SACX,OAAA;EAAA,SACA,SAAA;AAAA;;;AAMF;KAAY,YAAA,WAAuB,SAAA,eAAwB,SAAA;;;;iBAK3C,cAAA,CAAe,KAAA,WAAgB,KAAA,IAAS,YAAA;;;;UAWvC,oBAAA;EAAA,SACN,EAAA;EAAA,SACA,IAAA;EAAA,SACA,IAAA;AAAA;AAHX;;;AAAA,UASiB,aAAA;;WAEN,IAAA;;WAEA,QAAA,EAAU,YAAA;EAVV;EAAA,SAYA,WAAA;EANM;EAAA,SAQN,aAAA,WAAwB,oBAAA;AAAA;;;;;UAOlB,aAAA;;WAEN,QAAA;EATwB;EAAA,SAWxB,WAAA;EAJM;EAAA,SAMN,SAAA;AAAA;;;;UAUM,UAAA;EAAA,SACN,IAAA;EAAA,SACA,IAAA;AAAA;;;;UAMM,YAAA;EAAA,SACN,MAAA;AAAA;;;AAMX;UAAiB,aAAA;;WAEN,MAAA;;WAEA,UAAA;;WAEA,OAAA;;WAEA,QAAA;;WAEA,WAAA;;WAEA,OAAA;;WAEA,cAAA;EAYX;EAAA,SAVW,QAAA;;WAEA,kBAAA;;WAEA,WAAA;AAAA;;;;UAMM,cAAA;EAYN;EAAA,SAVA,MAAA;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/utils/package-manager.ts","../src/utils/file-system.ts","../src/utils/prompts.ts","../src/commands/create.ts","../src/commands/destroy.ts","../src/commands/widget-create.ts","../src/commands/doctor.ts","../src/utils/cloud-run.ts","../src/utils/fluid-api.ts","../src/utils/turso.ts","../src/index.ts"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import ora from "ora";
|
|
4
|
-
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
import path, { dirname, join, resolve } from "node:path";
|
|
5
5
|
import { copyFile, mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
6
6
|
import prompts from "prompts";
|
|
7
|
-
import { existsSync } from "node:fs";
|
|
7
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
9
|
import Handlebars from "handlebars";
|
|
10
10
|
import { failure, getErrorMessage, success } from "@fluid-app/fluid-cli";
|
|
11
11
|
import { execa } from "execa";
|
|
12
12
|
import fs from "fs-extra";
|
|
13
|
-
import path from "path";
|
|
13
|
+
import path$1 from "path";
|
|
14
14
|
import { config } from "dotenv";
|
|
15
15
|
//#region src/types.ts
|
|
16
16
|
/**
|
|
@@ -110,13 +110,13 @@ async function promptProjectConfig(projectName, options) {
|
|
|
110
110
|
}
|
|
111
111
|
//#endregion
|
|
112
112
|
//#region src/utils/file-system.ts
|
|
113
|
-
const _currentDir = dirname(fileURLToPath(import.meta.url));
|
|
113
|
+
const _currentDir$1 = dirname(fileURLToPath(import.meta.url));
|
|
114
114
|
/**
|
|
115
115
|
* Find the package root by walking up from the current directory to the nearest package.json.
|
|
116
116
|
* Works whether running from dist/ (bundled) or src/utils/ (tsx dev mode).
|
|
117
117
|
*/
|
|
118
118
|
function findPackageRoot() {
|
|
119
|
-
let dir = _currentDir;
|
|
119
|
+
let dir = _currentDir$1;
|
|
120
120
|
while (!existsSync(join(dir, "package.json"))) {
|
|
121
121
|
const parent = dirname(dir);
|
|
122
122
|
if (parent === dir) throw new Error("Could not find package root");
|
|
@@ -1314,7 +1314,7 @@ async function validateFluidApiKey(apiKey) {
|
|
|
1314
1314
|
* Read project name from package.json
|
|
1315
1315
|
*/
|
|
1316
1316
|
async function getProjectName(cwd) {
|
|
1317
|
-
const packageJsonPath = path.join(cwd, "package.json");
|
|
1317
|
+
const packageJsonPath = path$1.join(cwd, "package.json");
|
|
1318
1318
|
if (await fs.pathExists(packageJsonPath)) return (await fs.readJson(packageJsonPath)).name;
|
|
1319
1319
|
}
|
|
1320
1320
|
/**
|
|
@@ -1351,8 +1351,8 @@ const EXTRACT_FILENAME = "__fluid_extract_nav.ts";
|
|
|
1351
1351
|
* @param projectDir - The project root directory containing src/navigation.config.ts
|
|
1352
1352
|
*/
|
|
1353
1353
|
async function extractNavigation(projectDir) {
|
|
1354
|
-
const configPath = path.join(projectDir, "src", "navigation.config.ts");
|
|
1355
|
-
const extractFile = path.join(projectDir, EXTRACT_FILENAME);
|
|
1354
|
+
const configPath = path$1.join(projectDir, "src", "navigation.config.ts");
|
|
1355
|
+
const extractFile = path$1.join(projectDir, EXTRACT_FILENAME);
|
|
1356
1356
|
try {
|
|
1357
1357
|
const configSource = await fs.readFile(configPath, "utf-8");
|
|
1358
1358
|
if (!/export\s+(const|let)\s+navigation\s*=/.test(configSource)) return success(null);
|
|
@@ -1610,11 +1610,11 @@ async function syncNavigation(apiKey, codeItems) {
|
|
|
1610
1610
|
* Detect if the project is a fullstack template (has server entry)
|
|
1611
1611
|
*/
|
|
1612
1612
|
async function isFullstackProject(cwd) {
|
|
1613
|
-
return fs.pathExists(path.join(cwd, "src", "server", "index.ts"));
|
|
1613
|
+
return fs.pathExists(path$1.join(cwd, "src", "server", "index.ts"));
|
|
1614
1614
|
}
|
|
1615
1615
|
const deployCommand = new Command("deploy").description("Deploy the fullstack application to Cloud Run + Turso").option("--region <region>", "Cloud Run region", "us-central1").option("--gcp-project <id>", "GCP project ID (default: from gcloud config)").option("-p, --project <name>", "Service name override (default: from package.json)").option("--db-region <location>", "Turso database group location", "aws-us-east-1").option("--require-auth", "Require IAM authentication for the Cloud Run service (default: public)").option("--migrate", "Run database migrations (db:push) after successful deploy").option("--skip-local-build", "Skip the local Docker build check before deploying").option("--turso-org <slug>", "Turso organization slug (skips interactive org selection)").option("--fluid-company-api-key <key>", "Fluid company API key (skips env var lookup and prompt)").option("--skip-nav-sync", "Skip navigation sync from portal.config.ts").action(async (options) => {
|
|
1616
1616
|
const cwd = process.cwd();
|
|
1617
|
-
config({ path: path.join(cwd, ".env") });
|
|
1617
|
+
config({ path: path$1.join(cwd, ".env") });
|
|
1618
1618
|
console.log();
|
|
1619
1619
|
console.log(chalk.blue.bold("Fluid Deploy") + chalk.gray(" (Cloud Run + Turso)"));
|
|
1620
1620
|
console.log();
|
|
@@ -1646,7 +1646,7 @@ const deployCommand = new Command("deploy").description("Deploy the fullstack ap
|
|
|
1646
1646
|
process.exit(1);
|
|
1647
1647
|
}
|
|
1648
1648
|
spinner.succeed(`Fluid company: ${chalk.cyan(fluidResult.value.name)}`);
|
|
1649
|
-
if (!await fs.pathExists(path.join(cwd, "Dockerfile"))) {
|
|
1649
|
+
if (!await fs.pathExists(path$1.join(cwd, "Dockerfile"))) {
|
|
1650
1650
|
console.log(chalk.red("Error:") + " No Dockerfile found in current directory.");
|
|
1651
1651
|
console.log();
|
|
1652
1652
|
console.log("Fullstack projects created with the latest template include a Dockerfile.");
|
|
@@ -1845,7 +1845,7 @@ const deployCommand = new Command("deploy").description("Deploy the fullstack ap
|
|
|
1845
1845
|
console.log(chalk.gray("GCP Project: ") + chalk.cyan(deployResult.value.gcpProject));
|
|
1846
1846
|
console.log();
|
|
1847
1847
|
if (!options.skipNavSync) {
|
|
1848
|
-
const configPath = path.join(cwd, "src", "navigation.config.ts");
|
|
1848
|
+
const configPath = path$1.join(cwd, "src", "navigation.config.ts");
|
|
1849
1849
|
if (await fs.pathExists(configPath)) {
|
|
1850
1850
|
const navSpinner = ora("Extracting navigation from navigation.config.ts...").start();
|
|
1851
1851
|
const extractResult = await extractNavigation(cwd);
|
|
@@ -1936,7 +1936,7 @@ function registerDeployCommand(ctx) {
|
|
|
1936
1936
|
//#region src/commands/destroy.ts
|
|
1937
1937
|
const destroyCommand = new Command("destroy").description("Tear down deployed Cloud Run service and Turso database").option("--region <region>", "Cloud Run region", "us-central1").option("--gcp-project <id>", "GCP project ID (default: from gcloud config)").option("-p, --project <name>", "Service name override (default: from package.json)").option("--turso-org <slug>", "Turso organization slug (skips interactive org selection)").option("--fluid-company-api-key <key>", "Fluid company API key (skips env var lookup and prompt)").option("-y, --yes", "Skip confirmation prompt").action(async (options) => {
|
|
1938
1938
|
const cwd = process.cwd();
|
|
1939
|
-
config({ path: path.join(cwd, ".env") });
|
|
1939
|
+
config({ path: path$1.join(cwd, ".env") });
|
|
1940
1940
|
console.log();
|
|
1941
1941
|
console.log(chalk.red.bold("Fluid Destroy") + chalk.gray(" (Cloud Run + Turso)"));
|
|
1942
1942
|
console.log();
|
|
@@ -2072,6 +2072,354 @@ function registerDestroyCommand(ctx) {
|
|
|
2072
2072
|
ctx.program.addCommand(destroyCommand);
|
|
2073
2073
|
}
|
|
2074
2074
|
//#endregion
|
|
2075
|
+
//#region src/utils/widget-helpers.ts
|
|
2076
|
+
/**
|
|
2077
|
+
* Pure helper functions for widget scaffolding.
|
|
2078
|
+
* Extracted from widget-create command for testability.
|
|
2079
|
+
*/
|
|
2080
|
+
/**
|
|
2081
|
+
* Convert kebab-case name to PascalCase widget type.
|
|
2082
|
+
* e.g., "stock-ticker" → "StockTickerWidget"
|
|
2083
|
+
*/
|
|
2084
|
+
function toWidgetType(name) {
|
|
2085
|
+
const pascal = name.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
|
|
2086
|
+
return pascal.endsWith("Widget") ? pascal : `${pascal}Widget`;
|
|
2087
|
+
}
|
|
2088
|
+
/**
|
|
2089
|
+
* Derive the component name from a widget type.
|
|
2090
|
+
* Strips the trailing "Widget" suffix, but only if the result is non-empty
|
|
2091
|
+
* and starts with a letter (not a digit).
|
|
2092
|
+
* e.g., "StockTickerWidget" → "StockTicker"
|
|
2093
|
+
*/
|
|
2094
|
+
function toComponentName(widgetType) {
|
|
2095
|
+
if (widgetType === "Widget") return "Widget";
|
|
2096
|
+
return widgetType.replace(/Widget$/, "") || widgetType;
|
|
2097
|
+
}
|
|
2098
|
+
/**
|
|
2099
|
+
* Convert kebab-case name to a display name.
|
|
2100
|
+
* e.g., "stock-ticker" → "Stock Ticker"
|
|
2101
|
+
*/
|
|
2102
|
+
function toDisplayName(name) {
|
|
2103
|
+
return name.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" ");
|
|
2104
|
+
}
|
|
2105
|
+
/**
|
|
2106
|
+
* Convert kebab-case to camelCase.
|
|
2107
|
+
* e.g., "stock-ticker" → "stockTicker"
|
|
2108
|
+
*/
|
|
2109
|
+
function toCamelCase(name) {
|
|
2110
|
+
return name.replace(/-./g, (x) => x.charAt(1).toUpperCase());
|
|
2111
|
+
}
|
|
2112
|
+
/**
|
|
2113
|
+
* Validate a widget name for scaffold.
|
|
2114
|
+
* Must be kebab-case, no trailing/consecutive dashes, no bare "widget".
|
|
2115
|
+
*/
|
|
2116
|
+
function validateWidgetName(name) {
|
|
2117
|
+
if (!/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/.test(name)) return "Widget name must be kebab-case with no trailing or consecutive dashes (e.g., stock-ticker).";
|
|
2118
|
+
if (name === "widget") return "Widget name \"widget\" is reserved. Use a more descriptive name (e.g., custom-widget).";
|
|
2119
|
+
return null;
|
|
2120
|
+
}
|
|
2121
|
+
/**
|
|
2122
|
+
* Insert an import line after the last import in a source file.
|
|
2123
|
+
* Skips if the import already exists (prevents duplicates on re-scaffold).
|
|
2124
|
+
* Returns null if no imports exist in the source.
|
|
2125
|
+
*/
|
|
2126
|
+
function insertImport(source, importLine) {
|
|
2127
|
+
if (source.includes(importLine)) return source;
|
|
2128
|
+
const lastImportIndex = source.lastIndexOf("import ");
|
|
2129
|
+
if (lastImportIndex === -1) return null;
|
|
2130
|
+
const lineEnd = source.indexOf("\n", lastImportIndex);
|
|
2131
|
+
if (lineEnd === -1) return source + "\n" + importLine + "\n";
|
|
2132
|
+
return source.slice(0, lineEnd + 1) + importLine + "\n" + source.slice(lineEnd + 1);
|
|
2133
|
+
}
|
|
2134
|
+
/**
|
|
2135
|
+
* Insert a manifest reference into the customWidgets array.
|
|
2136
|
+
* Preserves developer comments. Skips if already present.
|
|
2137
|
+
* Returns null if the array pattern isn't found.
|
|
2138
|
+
*/
|
|
2139
|
+
function insertIntoCustomWidgets(source, camelName) {
|
|
2140
|
+
let matched = false;
|
|
2141
|
+
const result = source.replace(/export const customWidgets:\s*WidgetManifest\[\]\s*=\s*\[([^\]]*)\]/, (_match, inner) => {
|
|
2142
|
+
matched = true;
|
|
2143
|
+
const lines = inner.split("\n");
|
|
2144
|
+
const existingEntries = [];
|
|
2145
|
+
for (const line of lines) {
|
|
2146
|
+
const trimmed = line.trim();
|
|
2147
|
+
if (trimmed && !trimmed.startsWith("//")) existingEntries.push(trimmed.replace(/,$/, ""));
|
|
2148
|
+
}
|
|
2149
|
+
if (existingEntries.includes(camelName)) return _match;
|
|
2150
|
+
const commentLines = lines.filter((line) => line.trim().startsWith("//")).map((line) => line.trimEnd());
|
|
2151
|
+
return `export const customWidgets: WidgetManifest[] = [${commentLines.length > 0 ? "\n" + commentLines.join("\n") : ""}\n${[...existingEntries, camelName].map((e) => ` ${e},`).join("\n")}\n]`;
|
|
2152
|
+
});
|
|
2153
|
+
return matched ? result : null;
|
|
2154
|
+
}
|
|
2155
|
+
//#endregion
|
|
2156
|
+
//#region src/commands/widget-create.ts
|
|
2157
|
+
const createSubcommand = new Command("create").description("Scaffold a new custom widget").argument("<name>", "Widget name in kebab-case (e.g., stock-ticker)").option("-c, --category <category>", "Widget category for palette grouping", "components").action(async (name, options) => {
|
|
2158
|
+
const cwd = process.cwd();
|
|
2159
|
+
const widgetDir = path.join(cwd, "src", "widgets", name);
|
|
2160
|
+
const validationError = validateWidgetName(name);
|
|
2161
|
+
if (validationError) {
|
|
2162
|
+
console.error(chalk.red(`Error: ${validationError}`));
|
|
2163
|
+
process.exit(1);
|
|
2164
|
+
}
|
|
2165
|
+
if (!fs.existsSync(path.join(cwd, "src", "portal.config.ts"))) {
|
|
2166
|
+
console.error(chalk.red("Error: No src/portal.config.ts found."));
|
|
2167
|
+
console.error(chalk.yellow("Run this command from a Fluid portal project root."));
|
|
2168
|
+
process.exit(1);
|
|
2169
|
+
}
|
|
2170
|
+
if (fs.existsSync(widgetDir)) {
|
|
2171
|
+
console.error(chalk.red(`Error: Widget directory already exists: src/widgets/${name}`));
|
|
2172
|
+
process.exit(1);
|
|
2173
|
+
}
|
|
2174
|
+
const widgetType = toWidgetType(name);
|
|
2175
|
+
const componentName = toComponentName(widgetType);
|
|
2176
|
+
const displayName = toDisplayName(name);
|
|
2177
|
+
const category = options.category ?? "components";
|
|
2178
|
+
try {
|
|
2179
|
+
await fs.ensureDir(widgetDir);
|
|
2180
|
+
await fs.writeFile(path.join(widgetDir, "component.tsx"), `interface ${widgetType}Props {
|
|
2181
|
+
title?: string;
|
|
2182
|
+
}
|
|
2183
|
+
|
|
2184
|
+
export function ${componentName}({ title = "${displayName}" }: ${widgetType}Props) {
|
|
2185
|
+
return (
|
|
2186
|
+
<div className="p-4">
|
|
2187
|
+
<h3 className="text-lg font-semibold">{title}</h3>
|
|
2188
|
+
<p className="text-sm text-gray-500">
|
|
2189
|
+
Edit this widget in src/widgets/${name}/component.tsx
|
|
2190
|
+
</p>
|
|
2191
|
+
</div>
|
|
2192
|
+
);
|
|
2193
|
+
}
|
|
2194
|
+
`);
|
|
2195
|
+
await fs.writeFile(path.join(widgetDir, "manifest.ts"), `import type { WidgetManifest } from "@fluid-app/portal-core/registries";
|
|
2196
|
+
import { ${componentName} } from "./component";
|
|
2197
|
+
|
|
2198
|
+
export const manifest: WidgetManifest = {
|
|
2199
|
+
manifestVersion: 1,
|
|
2200
|
+
type: "${widgetType}",
|
|
2201
|
+
component: ${componentName},
|
|
2202
|
+
displayName: "${displayName}",
|
|
2203
|
+
description: "A custom ${displayName.toLowerCase()} widget",
|
|
2204
|
+
icon: "puzzle-piece",
|
|
2205
|
+
category: "${category}",
|
|
2206
|
+
propertySchema: {
|
|
2207
|
+
widgetType: "${widgetType}",
|
|
2208
|
+
displayName: "${displayName}",
|
|
2209
|
+
fields: [{ key: "title", label: "Title", type: "text" }],
|
|
2210
|
+
},
|
|
2211
|
+
defaultProps: {
|
|
2212
|
+
title: "${displayName}",
|
|
2213
|
+
},
|
|
2214
|
+
};
|
|
2215
|
+
`);
|
|
2216
|
+
await fs.writeFile(path.join(widgetDir, "index.ts"), `export { ${componentName} } from "./component";
|
|
2217
|
+
export { manifest } from "./manifest";
|
|
2218
|
+
`);
|
|
2219
|
+
} catch (err) {
|
|
2220
|
+
await fs.remove(widgetDir).catch(() => {});
|
|
2221
|
+
console.error(chalk.red("Error: Failed to scaffold widget files."));
|
|
2222
|
+
console.error(err);
|
|
2223
|
+
process.exit(1);
|
|
2224
|
+
}
|
|
2225
|
+
const configPath = path.join(cwd, "src", "portal.config.ts");
|
|
2226
|
+
const camelName = toCamelCase(name);
|
|
2227
|
+
const importLine = `import { manifest as ${camelName} } from "./widgets/${name}";`;
|
|
2228
|
+
const configSource = await fs.readFile(configPath, "utf-8");
|
|
2229
|
+
const withImport = insertImport(configSource, importLine);
|
|
2230
|
+
if (withImport === null) {
|
|
2231
|
+
console.warn(chalk.yellow("Warning: Could not find import statements in portal.config.ts. Add the import manually:"));
|
|
2232
|
+
console.warn(chalk.cyan(` ${importLine}`));
|
|
2233
|
+
}
|
|
2234
|
+
const updated = insertIntoCustomWidgets(withImport ?? configSource, camelName);
|
|
2235
|
+
if (updated === null) {
|
|
2236
|
+
console.warn(chalk.yellow("Warning: Could not find customWidgets array in portal.config.ts. Add the manifest manually:"));
|
|
2237
|
+
console.warn(chalk.cyan(` ${camelName}`));
|
|
2238
|
+
}
|
|
2239
|
+
if (updated !== null) await fs.writeFile(configPath, updated, "utf-8");
|
|
2240
|
+
else if (withImport !== null) await fs.writeFile(configPath, withImport, "utf-8");
|
|
2241
|
+
console.log();
|
|
2242
|
+
console.log(chalk.green("Created widget:") + ` src/widgets/${name}/`);
|
|
2243
|
+
console.log(chalk.gray(" component.tsx — React component"));
|
|
2244
|
+
console.log(chalk.gray(" manifest.ts — WidgetManifest"));
|
|
2245
|
+
console.log(chalk.gray(" index.ts — Re-exports"));
|
|
2246
|
+
console.log();
|
|
2247
|
+
if (updated !== null) {
|
|
2248
|
+
console.log(chalk.green("Registered") + ` in ${chalk.cyan("src/portal.config.ts")}`);
|
|
2249
|
+
console.log();
|
|
2250
|
+
}
|
|
2251
|
+
console.log(chalk.yellow("Next steps:"));
|
|
2252
|
+
console.log(` 1. Edit the component in ${chalk.cyan(`src/widgets/${name}/component.tsx`)}`);
|
|
2253
|
+
console.log(` 2. Customize the manifest fields in ${chalk.cyan(`src/widgets/${name}/manifest.ts`)}`);
|
|
2254
|
+
console.log(` 3. Run ${chalk.cyan("fluid dev")} to preview in the builder`);
|
|
2255
|
+
});
|
|
2256
|
+
const widgetCommand = new Command("widget").description("Manage custom portal widgets").addCommand(createSubcommand);
|
|
2257
|
+
function registerWidgetCommand(ctx) {
|
|
2258
|
+
ctx.program.addCommand(widgetCommand);
|
|
2259
|
+
}
|
|
2260
|
+
//#endregion
|
|
2261
|
+
//#region src/commands/doctor.ts
|
|
2262
|
+
/** Files that are managed by the SDK and should match the canonical template. */
|
|
2263
|
+
const INFRASTRUCTURE_FILES = [
|
|
2264
|
+
"index.html",
|
|
2265
|
+
"tsconfig.json",
|
|
2266
|
+
"vite.config.ts",
|
|
2267
|
+
".oxlintrc.json"
|
|
2268
|
+
];
|
|
2269
|
+
/** Entry files with expected content patterns after Phase 2 absorption. */
|
|
2270
|
+
const ENTRY_CHECKS = [{
|
|
2271
|
+
file: "src/main.tsx",
|
|
2272
|
+
expectedPattern: "createPortal",
|
|
2273
|
+
hint: "main.tsx should use createPortal() from @fluid-app/portal-sdk. Run \"fluid create\" to see the latest template."
|
|
2274
|
+
}, {
|
|
2275
|
+
file: "src/index.css",
|
|
2276
|
+
expectedPattern: "@fluid-app/portal-sdk/globals.css",
|
|
2277
|
+
hint: "index.css should import @fluid-app/portal-sdk/globals.css instead of inline theme tokens."
|
|
2278
|
+
}];
|
|
2279
|
+
/** Files that should NOT exist after Phase 2 (absorbed into SDK). */
|
|
2280
|
+
const REMOVED_FILES = [{
|
|
2281
|
+
file: "src/App.tsx",
|
|
2282
|
+
hint: "App.tsx is no longer needed — createPortal() handles the AppShell wiring internally."
|
|
2283
|
+
}, {
|
|
2284
|
+
file: "src/fluid.config.ts",
|
|
2285
|
+
hint: "fluid.config.ts is no longer needed — pass config overrides to createPortal() directly."
|
|
2286
|
+
}];
|
|
2287
|
+
function readFileOrNull(filePath) {
|
|
2288
|
+
try {
|
|
2289
|
+
return readFileSync(filePath, "utf-8");
|
|
2290
|
+
} catch {
|
|
2291
|
+
return null;
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
const _currentDir = dirname(fileURLToPath(import.meta.url));
|
|
2295
|
+
function findTemplateDir() {
|
|
2296
|
+
let dir = _currentDir;
|
|
2297
|
+
while (!existsSync(join(dir, "package.json"))) {
|
|
2298
|
+
const parent = dirname(dir);
|
|
2299
|
+
if (parent === dir) break;
|
|
2300
|
+
dir = parent;
|
|
2301
|
+
}
|
|
2302
|
+
const templateDir = join(dir, "templates");
|
|
2303
|
+
if (existsSync(join(templateDir, "base"))) return templateDir;
|
|
2304
|
+
return null;
|
|
2305
|
+
}
|
|
2306
|
+
function checkInfrastructureDrift(cwd, templateDir) {
|
|
2307
|
+
const diagnostics = [];
|
|
2308
|
+
for (const file of INFRASTRUCTURE_FILES) {
|
|
2309
|
+
const portalPath = join(cwd, file);
|
|
2310
|
+
const templatePath = join(templateDir, "base", file);
|
|
2311
|
+
if (!existsSync(portalPath)) {
|
|
2312
|
+
diagnostics.push({
|
|
2313
|
+
file,
|
|
2314
|
+
severity: "warn",
|
|
2315
|
+
message: `Missing file: ${file}`
|
|
2316
|
+
});
|
|
2317
|
+
continue;
|
|
2318
|
+
}
|
|
2319
|
+
if (!existsSync(templatePath)) continue;
|
|
2320
|
+
const portalContent = readFileOrNull(portalPath);
|
|
2321
|
+
const templateContent = readFileOrNull(templatePath);
|
|
2322
|
+
if (portalContent !== null && templateContent !== null) {
|
|
2323
|
+
if (portalContent.trim() !== templateContent.trim()) diagnostics.push({
|
|
2324
|
+
file,
|
|
2325
|
+
severity: "warn",
|
|
2326
|
+
message: `Content differs from canonical template. Review and update if needed.`
|
|
2327
|
+
});
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
return diagnostics;
|
|
2331
|
+
}
|
|
2332
|
+
function checkEntryPatterns(cwd) {
|
|
2333
|
+
const diagnostics = [];
|
|
2334
|
+
for (const check of ENTRY_CHECKS) {
|
|
2335
|
+
const content = readFileOrNull(join(cwd, check.file));
|
|
2336
|
+
if (content === null) {
|
|
2337
|
+
diagnostics.push({
|
|
2338
|
+
file: check.file,
|
|
2339
|
+
severity: "error",
|
|
2340
|
+
message: `Missing file. ${check.hint}`
|
|
2341
|
+
});
|
|
2342
|
+
continue;
|
|
2343
|
+
}
|
|
2344
|
+
if (!content.includes(check.expectedPattern)) diagnostics.push({
|
|
2345
|
+
file: check.file,
|
|
2346
|
+
severity: "warn",
|
|
2347
|
+
message: `Does not contain expected pattern "${check.expectedPattern}". ${check.hint}`
|
|
2348
|
+
});
|
|
2349
|
+
}
|
|
2350
|
+
return diagnostics;
|
|
2351
|
+
}
|
|
2352
|
+
function checkRemovedFiles(cwd) {
|
|
2353
|
+
const diagnostics = [];
|
|
2354
|
+
for (const check of REMOVED_FILES) if (existsSync(join(cwd, check.file))) diagnostics.push({
|
|
2355
|
+
file: check.file,
|
|
2356
|
+
severity: "info",
|
|
2357
|
+
message: `File can be removed. ${check.hint}`
|
|
2358
|
+
});
|
|
2359
|
+
return diagnostics;
|
|
2360
|
+
}
|
|
2361
|
+
function formatDiagnostic(d) {
|
|
2362
|
+
return ` ${d.severity === "error" ? chalk.red("ERROR") : d.severity === "warn" ? chalk.yellow(" WARN") : chalk.blue(" INFO")} ${chalk.bold(d.file)}\n ${chalk.dim(d.message)}`;
|
|
2363
|
+
}
|
|
2364
|
+
const doctorCommand = new Command("doctor").description("Check portal for scaffold drift and report stale infrastructure files").action(async () => {
|
|
2365
|
+
const cwd = process.cwd();
|
|
2366
|
+
const packageJsonPath = join(cwd, "package.json");
|
|
2367
|
+
if (!existsSync(packageJsonPath)) {
|
|
2368
|
+
console.error(chalk.red("Error: No package.json found in current directory"));
|
|
2369
|
+
console.error(chalk.yellow("Make sure you're in a Fluid portal project directory"));
|
|
2370
|
+
process.exit(1);
|
|
2371
|
+
}
|
|
2372
|
+
let packageJson;
|
|
2373
|
+
try {
|
|
2374
|
+
packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
2375
|
+
} catch {
|
|
2376
|
+
console.error(chalk.red("Error: Could not parse package.json"));
|
|
2377
|
+
console.error(chalk.yellow("Ensure package.json contains valid JSON"));
|
|
2378
|
+
process.exit(1);
|
|
2379
|
+
}
|
|
2380
|
+
if (!{
|
|
2381
|
+
...packageJson.dependencies,
|
|
2382
|
+
...packageJson.devDependencies
|
|
2383
|
+
}["@fluid-app/portal-sdk"]) {
|
|
2384
|
+
console.error(chalk.red("Error: @fluid-app/portal-sdk not found in dependencies"));
|
|
2385
|
+
console.error(chalk.yellow("This command must be run from a Fluid portal project directory"));
|
|
2386
|
+
process.exit(1);
|
|
2387
|
+
}
|
|
2388
|
+
console.log();
|
|
2389
|
+
console.log(chalk.bold("Fluid Portal Doctor"));
|
|
2390
|
+
console.log(chalk.dim("Checking for scaffold drift...\n"));
|
|
2391
|
+
const diagnostics = [];
|
|
2392
|
+
diagnostics.push(...checkEntryPatterns(cwd));
|
|
2393
|
+
diagnostics.push(...checkRemovedFiles(cwd));
|
|
2394
|
+
const templateDir = findTemplateDir();
|
|
2395
|
+
if (templateDir) diagnostics.push(...checkInfrastructureDrift(cwd, templateDir));
|
|
2396
|
+
else console.log(chalk.dim(" (Skipping infrastructure diff — template files not available)\n"));
|
|
2397
|
+
if (diagnostics.length === 0) {
|
|
2398
|
+
console.log(chalk.green(" All clear — no drift detected.\n"));
|
|
2399
|
+
return;
|
|
2400
|
+
}
|
|
2401
|
+
const errors = diagnostics.filter((d) => d.severity === "error");
|
|
2402
|
+
const warns = diagnostics.filter((d) => d.severity === "warn");
|
|
2403
|
+
const infos = diagnostics.filter((d) => d.severity === "info");
|
|
2404
|
+
for (const d of [
|
|
2405
|
+
...errors,
|
|
2406
|
+
...warns,
|
|
2407
|
+
...infos
|
|
2408
|
+
]) {
|
|
2409
|
+
console.log(formatDiagnostic(d));
|
|
2410
|
+
console.log();
|
|
2411
|
+
}
|
|
2412
|
+
const parts = [];
|
|
2413
|
+
if (errors.length > 0) parts.push(chalk.red(`${errors.length} error(s)`));
|
|
2414
|
+
if (warns.length > 0) parts.push(chalk.yellow(`${warns.length} warning(s)`));
|
|
2415
|
+
if (infos.length > 0) parts.push(chalk.blue(`${infos.length} info`));
|
|
2416
|
+
console.log(` ${parts.join(", ")}\n`);
|
|
2417
|
+
if (errors.length > 0) process.exit(1);
|
|
2418
|
+
});
|
|
2419
|
+
function registerDoctorCommand(ctx) {
|
|
2420
|
+
ctx.program.addCommand(doctorCommand);
|
|
2421
|
+
}
|
|
2422
|
+
//#endregion
|
|
2075
2423
|
//#region src/index.ts
|
|
2076
2424
|
const plugin = {
|
|
2077
2425
|
name: "fluid-cli-portal",
|
|
@@ -2082,9 +2430,11 @@ const plugin = {
|
|
|
2082
2430
|
registerBuildCommand(ctx);
|
|
2083
2431
|
registerDeployCommand(ctx);
|
|
2084
2432
|
registerDestroyCommand(ctx);
|
|
2433
|
+
registerWidgetCommand(ctx);
|
|
2434
|
+
registerDoctorCommand(ctx);
|
|
2085
2435
|
}
|
|
2086
2436
|
};
|
|
2087
2437
|
//#endregion
|
|
2088
|
-
export { CLOUD_RUN_ERRORS, FILE_SYSTEM_ERRORS, FLUID_API_ERROR, TEMPLATES, TURSO_ERROR, copyTemplate, copyTemplateSafe, createCommand, createDatabase, createDatabaseToken, createDirectory, createDirectorySafe, plugin as default, deleteCloudRunService, deleteDatabase, deployToCloudRun, destroyCommand, directoryExists, ensureGroup, fetchLocations, fileExists, getGcpProject, getInstallCommand, getRunCommand, getSdkVersion, getSdkVersionSafe, getTemplatePaths, installDependencies, isTemplateName, parseOrgList, pathExists, promptProjectConfig, provisionDatabase, readFileSafe, resolveFluidApiKey, resolveTursoConfig, runPackageManager, validateFluidApiKey, validateGcloudAuth, validateGcloudInstalled, validateLocation, validateTursoConfig, writeFileSafe };
|
|
2438
|
+
export { CLOUD_RUN_ERRORS, FILE_SYSTEM_ERRORS, FLUID_API_ERROR, TEMPLATES, TURSO_ERROR, copyTemplate, copyTemplateSafe, createCommand, createDatabase, createDatabaseToken, createDirectory, createDirectorySafe, plugin as default, deleteCloudRunService, deleteDatabase, deployToCloudRun, destroyCommand, directoryExists, doctorCommand, ensureGroup, fetchLocations, fileExists, getGcpProject, getInstallCommand, getRunCommand, getSdkVersion, getSdkVersionSafe, getTemplatePaths, installDependencies, isTemplateName, parseOrgList, pathExists, promptProjectConfig, provisionDatabase, readFileSafe, resolveFluidApiKey, resolveTursoConfig, runPackageManager, validateFluidApiKey, validateGcloudAuth, validateGcloudInstalled, validateLocation, validateTursoConfig, widgetCommand, writeFileSafe };
|
|
2089
2439
|
|
|
2090
2440
|
//# sourceMappingURL=index.mjs.map
|