From 0a69501e58a12cc1610e720e967dfeec1f97f793 Mon Sep 17 00:00:00 2001 From: Paul Fitzpatrick Date: Tue, 5 Dec 2023 11:51:22 -0500 Subject: [PATCH] Change how a builtin custom widget bundle is found (#783) This change makes builtin custom widget bundles work on grist-electron, by finding the package in a slightly more flexible way. It also includes a related change to make a widget manifest fetched from the network optional if a flag is present, with an error being logged rather than thrown. This could make it harder to track down why custom widgets aren't available, but makes it easier to make grist-electron work (including calendars) when the network is shut off. Ideally we'd do something fancier when we can. --- app/server/lib/FlexServer.ts | 10 ++++++++-- app/server/lib/WidgetRepository.ts | 21 +++++++++++++++------ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/app/server/lib/FlexServer.ts b/app/server/lib/FlexServer.ts index 277d671e..76667642 100644 --- a/app/server/lib/FlexServer.ts +++ b/app/server/lib/FlexServer.ts @@ -1999,9 +1999,15 @@ export class FlexServer implements GristServer { const userRoot = path.resolve(process.env.GRIST_USER_ROOT || getAppPathTo(this.appRoot, '.grist')); this.info.push(['userRoot', userRoot]); // Some custom widgets may be included as an npm package called @gristlabs/grist-widget. + // The package doesn't actually contain node code, but should be in the same vicinity + // as other packages that do, so we can use require.resolve on one of them to find it. + // This seems a little overcomplicated, but works well when grist-core is bundled within + // a larger project like grist-electron. + // TODO: maybe add a little node code to @gristlabs/grist-widget so it can be resolved + // directly? + const gristLabsModules = path.dirname(path.dirname(require.resolve('@gristlabs/express-session'))); const bundledRoot = isAffirmative(process.env.GRIST_SKIP_BUNDLED_WIDGETS) ? undefined : path.join( - getAppPathTo(this.appRoot, 'node_modules'), - '@gristlabs', 'grist-widget', 'dist' + gristLabsModules, 'grist-widget', 'dist' ); this.info.push(['bundledRoot', bundledRoot]); const pluginManager = new PluginManager(this.appRoot, userRoot, bundledRoot); diff --git a/app/server/lib/WidgetRepository.ts b/app/server/lib/WidgetRepository.ts index f79a049f..d94ed92b 100644 --- a/app/server/lib/WidgetRepository.ts +++ b/app/server/lib/WidgetRepository.ts @@ -4,7 +4,7 @@ import * as fse from 'fs-extra'; import fetch from 'node-fetch'; import * as path from 'path'; import {ApiError} from 'app/common/ApiError'; -import {removeTrailingSlash} from 'app/common/gutil'; +import {isAffirmative, removeTrailingSlash} from 'app/common/gutil'; import {GristServer} from 'app/server/lib/GristServer'; import LRUCache from 'lru-cache'; import * as url from 'url'; @@ -96,7 +96,8 @@ export class CombinedWidgetRepository implements IWidgetRepository { * Repository that gets a list of widgets from a URL. */ export class UrlWidgetRepository implements IWidgetRepository { - constructor(private _staticUrl = STATIC_URL) {} + constructor(private _staticUrl = STATIC_URL, + private _required: boolean = true) {} public async getWidgets(): Promise { if (!this._staticUrl) { @@ -126,10 +127,16 @@ export class UrlWidgetRepository implements IWidgetRepository { fixUrls(widgets, this._staticUrl); return widgets; } catch (err) { - if (!(err instanceof ApiError)) { - throw new ApiError(String(err), 500); + if (this._required) { + if (!(err instanceof ApiError)) { + throw new ApiError(String(err), 500); + } + throw err; + } else { + log.error("WidgetRepository: Error fetching widget list - " + + String(err)); + return []; } - throw err; } } } @@ -177,7 +184,9 @@ export class WidgetRepositoryImpl implements IWidgetRepository { const repos: IWidgetRepository[] = []; this._staticUrl = overrideUrl ?? STATIC_URL; if (this._staticUrl) { - this._urlWidgets = new UrlWidgetRepository(this._staticUrl); + const optional = isAffirmative(process.env.GRIST_WIDGET_LIST_URL_OPTIONAL); + this._urlWidgets = new UrlWidgetRepository(this._staticUrl, + !optional); repos.push(this._urlWidgets); } if (this._diskWidgets) { repos.push(this._diskWidgets); }