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); }