From b3b8eddfb4f27a8ab67dd3b6e7910a92691012cd Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Tue, 23 May 2023 15:13:40 +0200 Subject: [PATCH 1/3] load managerHead in core-common preset, load it via preset.apply, so it can be overloaded by user in main.ts --- code/lib/builder-manager/src/utils/data.ts | 5 ++--- code/lib/core-server/src/presets/common-preset.ts | 4 ++++ code/lib/core-server/src/utils/safeResolve.ts | 7 +++++++ code/lib/types/src/modules/core-common.ts | 7 +++++++ 4 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 code/lib/core-server/src/utils/safeResolve.ts diff --git a/code/lib/builder-manager/src/utils/data.ts b/code/lib/builder-manager/src/utils/data.ts index b72b7120a3bd..5e315c92e9f8 100644 --- a/code/lib/builder-manager/src/utils/data.ts +++ b/code/lib/builder-manager/src/utils/data.ts @@ -1,11 +1,10 @@ -import { basename, join } from 'path'; +import { basename } from 'path'; import type { DocsOptions, Options } from '@storybook/types'; import { getRefs } from '@storybook/core-common'; import { readTemplate } from './template'; // eslint-disable-next-line import/no-cycle import { executor, getConfig } from '../index'; -import { safeResolve } from './safeResolve'; export const getData = async (options: Options) => { const refs = getRefs(options); @@ -16,7 +15,7 @@ export const getData = async (options: Options) => { const title = options.presets.apply('title'); const docsOptions = options.presets.apply('docs', {}); const template = readTemplate('template.ejs'); - const customHead = safeResolve(join(options.configDir, 'manager-head.html')); + const customHead = options.presets.apply('managerHead'); // we await these, because crucially if these fail, we want to bail out asap const [instance, config] = await Promise.all([ diff --git a/code/lib/core-server/src/presets/common-preset.ts b/code/lib/core-server/src/presets/common-preset.ts index 7b253ac65a86..432c014ff14e 100644 --- a/code/lib/core-server/src/presets/common-preset.ts +++ b/code/lib/core-server/src/presets/common-preset.ts @@ -20,6 +20,7 @@ import { join } from 'path'; import { dedent } from 'ts-dedent'; import { parseStaticDir } from '../utils/server-statics'; import { defaultStaticDirs } from '../utils/constants'; +import { safeResolve } from '../utils/safeResolve'; const defaultFavicon = require.resolve('@storybook/core-server/public/favicon.svg'); @@ -217,3 +218,6 @@ export const docs = ( ...docsOptions, docsMode, }); + +export const managerHead = (_: any, options: Options) => + safeResolve(join(options.configDir, 'manager-head.html')); diff --git a/code/lib/core-server/src/utils/safeResolve.ts b/code/lib/core-server/src/utils/safeResolve.ts new file mode 100644 index 000000000000..b166a22bced4 --- /dev/null +++ b/code/lib/core-server/src/utils/safeResolve.ts @@ -0,0 +1,7 @@ +export const safeResolve = (path: string) => { + try { + return Promise.resolve(require.resolve(path)); + } catch (e) { + return Promise.resolve(false as const); + } +}; diff --git a/code/lib/types/src/modules/core-common.ts b/code/lib/types/src/modules/core-common.ts index f45ee3b1d00c..c9b37e783262 100644 --- a/code/lib/types/src/modules/core-common.ts +++ b/code/lib/types/src/modules/core-common.ts @@ -396,6 +396,13 @@ export interface StorybookConfig { * @example '.storybook/index.ejs' */ previewMainTemplate?: string; + + /** + * Programmatically modify the preview head/body HTML. + * The managerHead function accept a string, + * which is the existing head, and return a modified string. + */ + managerHead?: PresetValue; } export type PresetValue = T | ((config: T, options: Options) => T | Promise); From f32a94fa389bdb38385867a8de25856558a2b539 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Wed, 24 May 2023 09:40:34 +0200 Subject: [PATCH 2/3] make preset value be the contents of the file rather then the location --- code/lib/builder-manager/src/utils/template.ts | 3 +-- .../core-server/src/presets/common-preset.ts | 17 ++++++++++++++--- code/lib/core-server/src/utils/safeResolve.ts | 7 ------- code/lib/types/src/modules/core-common.ts | 4 ++-- 4 files changed, 17 insertions(+), 14 deletions(-) delete mode 100644 code/lib/core-server/src/utils/safeResolve.ts diff --git a/code/lib/builder-manager/src/utils/template.ts b/code/lib/builder-manager/src/utils/template.ts index b959817cfa04..cc7237df9380 100644 --- a/code/lib/builder-manager/src/utils/template.ts +++ b/code/lib/builder-manager/src/utils/template.ts @@ -60,7 +60,6 @@ export const renderHTML = async ( docsOptions: Promise, { versionCheck, releaseNotesData, previewUrl, configType }: Options ) => { - const customHeadRef = await customHead; const titleRef = await title; const templateRef = await template; @@ -79,6 +78,6 @@ export const renderHTML = async ( RELEASE_NOTES_DATA: JSON.stringify(JSON.stringify(releaseNotesData), null, 2), PREVIEW_URL: JSON.stringify(previewUrl, null, 2), // global preview URL }, - head: customHeadRef ? await fs.readFile(customHeadRef, 'utf8') : '', + head: (await customHead) || '', }); }; diff --git a/code/lib/core-server/src/presets/common-preset.ts b/code/lib/core-server/src/presets/common-preset.ts index 432c014ff14e..ec821213808a 100644 --- a/code/lib/core-server/src/presets/common-preset.ts +++ b/code/lib/core-server/src/presets/common-preset.ts @@ -20,7 +20,9 @@ import { join } from 'path'; import { dedent } from 'ts-dedent'; import { parseStaticDir } from '../utils/server-statics'; import { defaultStaticDirs } from '../utils/constants'; -import { safeResolve } from '../utils/safeResolve'; + +const interpolate = (string: string, data: Record = {}) => + Object.entries(data).reduce((acc, [k, v]) => acc.replace(new RegExp(`%${k}%`, 'g'), v), string); const defaultFavicon = require.resolve('@storybook/core-server/public/favicon.svg'); @@ -219,5 +221,14 @@ export const docs = ( docsMode, }); -export const managerHead = (_: any, options: Options) => - safeResolve(join(options.configDir, 'manager-head.html')); +export const managerHead = async (_: any, options: Options) => { + const location = join(options.configDir, 'manager-head.html'); + if (await pathExists(location)) { + const contents = readFile(location, 'utf-8'); + const interpolations = options.presets.apply>('env'); + + return interpolate(await contents, await interpolations); + } + + return ''; +}; diff --git a/code/lib/core-server/src/utils/safeResolve.ts b/code/lib/core-server/src/utils/safeResolve.ts deleted file mode 100644 index b166a22bced4..000000000000 --- a/code/lib/core-server/src/utils/safeResolve.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const safeResolve = (path: string) => { - try { - return Promise.resolve(require.resolve(path)); - } catch (e) { - return Promise.resolve(false as const); - } -}; diff --git a/code/lib/types/src/modules/core-common.ts b/code/lib/types/src/modules/core-common.ts index c9b37e783262..caaa96db041c 100644 --- a/code/lib/types/src/modules/core-common.ts +++ b/code/lib/types/src/modules/core-common.ts @@ -389,7 +389,7 @@ export interface StorybookConfig { previewBody?: PresetValue; /** - * Programatically override the preview's main page template. + * Programmatically override the preview's main page template. * This should return a reference to a file containing an `.ejs` template * that will be interpolated with environment variables. * @@ -400,7 +400,7 @@ export interface StorybookConfig { /** * Programmatically modify the preview head/body HTML. * The managerHead function accept a string, - * which is the existing head, and return a modified string. + * which is the existing head content, and return a modified string. */ managerHead?: PresetValue; } From 4063303cf6d817504ef0e28bb9066bff2187a319 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Wed, 24 May 2023 09:59:15 +0200 Subject: [PATCH 3/3] cleanup --- .../lib/builder-manager/src/utils/template.ts | 26 +------------------ 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/code/lib/builder-manager/src/utils/template.ts b/code/lib/builder-manager/src/utils/template.ts index cc7237df9380..7c9f873794f4 100644 --- a/code/lib/builder-manager/src/utils/template.ts +++ b/code/lib/builder-manager/src/utils/template.ts @@ -1,13 +1,10 @@ -import path, { dirname, join } from 'path'; +import { dirname, join } from 'path'; import fs from 'fs-extra'; import { render } from 'ejs'; import type { DocsOptions, Options, Ref } from '@storybook/types'; -const interpolate = (string: string, data: Record = {}) => - Object.entries(data).reduce((acc, [k, v]) => acc.replace(new RegExp(`%${k}%`, 'g'), v), string); - export const getTemplatePath = async (template: string) => { return join( dirname(require.resolve('@storybook/builder-manager/package.json')), @@ -17,32 +14,11 @@ export const getTemplatePath = async (template: string) => { }; export const readTemplate = async (template: string) => { - // eslint-disable-next-line @typescript-eslint/no-shadow const path = await getTemplatePath(template); return fs.readFile(path, 'utf8'); }; -export async function getManagerHeadTemplate( - configDirPath: string, - interpolations: Record -) { - const head = await fs - .pathExists(path.resolve(configDirPath, 'manager-head.html')) - .then | false>((exists) => { - if (exists) { - return fs.readFile(path.resolve(configDirPath, 'manager-head.html'), 'utf8'); - } - return false; - }); - - if (!head) { - return ''; - } - - return interpolate(head, interpolations); -} - export async function getManagerMainTemplate() { return getTemplatePath(`manager.ejs`); }