@@ -157,14 +157,25 @@ export interface IStorybookPluginOptions {
157157 captureWebpackStats ?: boolean ;
158158}
159159
160- interface IRunStorybookOptions {
160+ interface IRunStorybookOptions extends IPrepareStorybookOptions {
161+ logger : IScopedLogger ;
162+ isServeMode : boolean ;
161163 workingDirectory : string ;
162164 resolvedModulePath : string ;
163165 outputFolder : string | undefined ;
164166 moduleDefaultArgs : string [ ] ;
165167 verbose : boolean ;
166168}
167169
170+ interface IPrepareStorybookOptions extends IStorybookPluginOptions {
171+ logger : IScopedLogger ;
172+ taskSession : IHeftTaskSession ;
173+ heftConfiguration : HeftConfiguration ;
174+ isServeMode : boolean ;
175+ isTestMode : boolean ;
176+ isDocsMode : boolean ;
177+ }
178+
168179const DEFAULT_STORYBOOK_VERSION : StorybookCliVersion = StorybookCliVersion . STORYBOOK7 ;
169180const DEFAULT_STORYBOOK_CLI_CONFIG : Record < StorybookCliVersion , IStorybookCliCallingConfig > = {
170181 [ StorybookCliVersion . STORYBOOK6 ] : {
@@ -190,14 +201,12 @@ const DEFAULT_STORYBOOK_CLI_CONFIG: Record<StorybookCliVersion, IStorybookCliCal
190201 }
191202} ;
192203
204+ const STORYBOOK_FLAG_NAME : '--storybook' = '--storybook' ;
193205const STORYBOOK_TEST_FLAG_NAME : '--storybook-test' = '--storybook-test' ;
206+ const DOCS_FLAG_NAME : '--docs' = '--docs' ;
194207
195208/** @public */
196209export default class StorybookPlugin implements IHeftTaskPlugin < IStorybookPluginOptions > {
197- private _logger ! : IScopedLogger ;
198- private _isServeMode : boolean = false ;
199- private _isTestMode : boolean = false ;
200-
201210 /**
202211 * Generate typings for Sass files before TypeScript compilation.
203212 */
@@ -206,11 +215,12 @@ export default class StorybookPlugin implements IHeftTaskPlugin<IStorybookPlugin
206215 heftConfiguration : HeftConfiguration ,
207216 options : IStorybookPluginOptions
208217 ) : void {
209- this . _logger = taskSession . logger ;
218+ const logger : IScopedLogger = taskSession . logger ;
210219 const storybookParameter : CommandLineFlagParameter =
211- taskSession . parameters . getFlagParameter ( '--storybook' ) ;
220+ taskSession . parameters . getFlagParameter ( STORYBOOK_FLAG_NAME ) ;
212221 const storybookTestParameter : CommandLineFlagParameter =
213222 taskSession . parameters . getFlagParameter ( STORYBOOK_TEST_FLAG_NAME ) ;
223+ const docsParameter : CommandLineFlagParameter = taskSession . parameters . getFlagParameter ( DOCS_FLAG_NAME ) ;
214224
215225 const parseResult : IParsedPackageNameOrError = PackageName . tryParse ( options . storykitPackageName ) ;
216226 if ( parseResult . error ) {
@@ -221,27 +231,23 @@ export default class StorybookPlugin implements IHeftTaskPlugin<IStorybookPlugin
221231 ) ;
222232 }
223233
224- if ( storybookTestParameter . value ) {
225- this . _isTestMode = true ;
226- }
227-
228234 // Only tap if the --storybook flag is present.
229235 if ( storybookParameter . value ) {
230236 const configureWebpackTap : ( ) => Promise < false > = async ( ) => {
231237 // Discard Webpack's configuration to prevent Webpack from running
232- this . _logger . terminal . writeLine (
238+ logger . terminal . writeLine (
233239 'The command line includes "--storybook", redirecting Webpack to Storybook'
234240 ) ;
235241 return false ;
236242 } ;
237243
244+ let isServeMode : boolean = false ;
238245 taskSession . requestAccessToPluginByName (
239246 '@rushstack/heft-webpack4-plugin' ,
240247 WEBPACK4_PLUGIN_NAME ,
241248 ( accessor : IWebpack4PluginAccessor ) => {
242- if ( accessor . parameters . isServeMode ) {
243- this . _isServeMode = true ;
244- }
249+ isServeMode = accessor . parameters . isServeMode ;
250+
245251 // Discard Webpack's configuration to prevent Webpack from running only when performing Storybook build
246252 accessor . hooks . onLoadConfiguration . tapPromise ( PLUGIN_NAME , configureWebpackTap ) ;
247253 }
@@ -251,43 +257,49 @@ export default class StorybookPlugin implements IHeftTaskPlugin<IStorybookPlugin
251257 '@rushstack/heft-webpack5-plugin' ,
252258 WEBPACK5_PLUGIN_NAME ,
253259 ( accessor : IWebpack5PluginAccessor ) => {
254- if ( accessor . parameters . isServeMode ) {
255- this . _isServeMode = true ;
256- }
260+ isServeMode = accessor . parameters . isServeMode ;
261+
257262 // Discard Webpack's configuration to prevent Webpack from running only when performing Storybook build
258263 accessor . hooks . onLoadConfiguration . tapPromise ( PLUGIN_NAME , configureWebpackTap ) ;
259264 }
260265 ) ;
261266
262267 taskSession . hooks . run . tapPromise ( PLUGIN_NAME , async ( runOptions : IHeftTaskRunHookOptions ) => {
263- const runStorybookOptions : IRunStorybookOptions = await this . _prepareStorybookAsync (
268+ const runStorybookOptions : IRunStorybookOptions = await this . _prepareStorybookAsync ( {
269+ logger,
264270 taskSession,
265271 heftConfiguration,
266- options
267- ) ;
272+ isServeMode,
273+ isTestMode : storybookTestParameter . value ,
274+ isDocsMode : docsParameter . value ,
275+ ...options
276+ } ) ;
268277 await this . _runStorybookAsync ( runStorybookOptions , options ) ;
269278 } ) ;
270279 }
271280 }
272281
273- private async _prepareStorybookAsync (
274- taskSession : IHeftTaskSession ,
275- heftConfiguration : HeftConfiguration ,
276- options : IStorybookPluginOptions
277- ) : Promise < IRunStorybookOptions > {
278- const { storykitPackageName, staticBuildOutputFolder } = options ;
282+ private async _prepareStorybookAsync ( options : IPrepareStorybookOptions ) : Promise < IRunStorybookOptions > {
283+ const {
284+ logger,
285+ taskSession,
286+ heftConfiguration,
287+ storykitPackageName,
288+ staticBuildOutputFolder,
289+ isTestMode
290+ } = options ;
279291 const storybookCliVersion : `${StorybookCliVersion } ` = this . _getStorybookVersion ( options ) ;
280292 const storyBookCliConfig : IStorybookCliCallingConfig = DEFAULT_STORYBOOK_CLI_CONFIG [ storybookCliVersion ] ;
281293 const cliPackageName : string = options . cliPackageName ?? storyBookCliConfig . packageName ;
282294 const buildMode : StorybookBuildMode = taskSession . parameters . watch
283295 ? StorybookBuildMode . WATCH
284296 : StorybookBuildMode . BUILD ;
285297
286- if ( buildMode === StorybookBuildMode . WATCH && this . _isTestMode ) {
298+ if ( buildMode === StorybookBuildMode . WATCH && isTestMode ) {
287299 throw new Error ( `The ${ STORYBOOK_TEST_FLAG_NAME } flag is not supported in watch mode` ) ;
288300 }
289301 if (
290- this . _isTestMode &&
302+ isTestMode &&
291303 ( storybookCliVersion === StorybookCliVersion . STORYBOOK6 ||
292304 storybookCliVersion === StorybookCliVersion . STORYBOOK7 )
293305 ) {
@@ -296,7 +308,7 @@ export default class StorybookPlugin implements IHeftTaskPlugin<IStorybookPlugin
296308 ) ;
297309 }
298310
299- this . _logger . terminal . writeVerboseLine ( `Probing for "${ storykitPackageName } "` ) ;
311+ logger . terminal . writeVerboseLine ( `Probing for "${ storykitPackageName } "` ) ;
300312 // Example: "/path/to/my-project/node_modules/my-storykit"
301313 let storykitFolderPath : string ;
302314 try {
@@ -308,9 +320,9 @@ export default class StorybookPlugin implements IHeftTaskPlugin<IStorybookPlugin
308320 throw new Error ( `The ${ taskSession . taskName } task cannot start: ` + ( ex as Error ) . message ) ;
309321 }
310322
311- this . _logger . terminal . writeVerboseLine ( `Found "${ storykitPackageName } " in ` + storykitFolderPath ) ;
323+ logger . terminal . writeVerboseLine ( `Found "${ storykitPackageName } " in ` + storykitFolderPath ) ;
312324
313- this . _logger . terminal . writeVerboseLine ( `Probing for "${ cliPackageName } " in "${ storykitPackageName } "` ) ;
325+ logger . terminal . writeVerboseLine ( `Probing for "${ cliPackageName } " in "${ storykitPackageName } "` ) ;
314326 // Example: "/path/to/my-project/node_modules/my-storykit/node_modules/@storybook/cli"
315327 let storyBookCliPackage : string ;
316328 try {
@@ -322,7 +334,7 @@ export default class StorybookPlugin implements IHeftTaskPlugin<IStorybookPlugin
322334 throw new Error ( `The ${ taskSession . taskName } task cannot start: ` + ( ex as Error ) . message ) ;
323335 }
324336
325- this . _logger . terminal . writeVerboseLine ( `Found "${ cliPackageName } " in ` + storyBookCliPackage ) ;
337+ logger . terminal . writeVerboseLine ( `Found "${ cliPackageName } " in ` + storyBookCliPackage ) ;
326338
327339 const storyBookPackagePackageJsonFile : string = path . join ( storyBookCliPackage , FileConstants . PackageJson ) ;
328340 const packageJson : IPackageJson = await JsonFile . loadAsync ( storyBookPackagePackageJsonFile ) ;
@@ -333,7 +345,7 @@ export default class StorybookPlugin implements IHeftTaskPlugin<IStorybookPlugin
333345 }
334346 const [ moduleExecutableName , ...moduleDefaultArgs ] = storyBookCliConfig . command [ buildMode ] ;
335347 const modulePath : string | undefined = packageJson . bin [ moduleExecutableName ] ;
336- this . _logger . terminal . writeVerboseLine (
348+ logger . terminal . writeVerboseLine (
337349 `Found storybook "${ modulePath } " for "${ buildMode } " mode in "${ cliPackageName } "`
338350 ) ;
339351
@@ -353,12 +365,12 @@ export default class StorybookPlugin implements IHeftTaskPlugin<IStorybookPlugin
353365 buildMode === StorybookBuildMode . WATCH ? undefined : staticBuildOutputFolder ;
354366
355367 if ( ! modulePath ) {
356- this . _logger . terminal . writeVerboseLine (
368+ logger . terminal . writeVerboseLine (
357369 'No matching module path option specified in heft.json, so bundling will proceed without Storybook'
358370 ) ;
359371 }
360372
361- this . _logger . terminal . writeVerboseLine ( `Resolving modulePath "${ modulePath } "` ) ;
373+ logger . terminal . writeVerboseLine ( `Resolving modulePath "${ modulePath } "` ) ;
362374 let resolvedModulePath : string ;
363375 try {
364376 resolvedModulePath = Import . resolveModule ( {
@@ -368,7 +380,7 @@ export default class StorybookPlugin implements IHeftTaskPlugin<IStorybookPlugin
368380 } catch ( ex ) {
369381 throw new Error ( `The ${ taskSession . taskName } task cannot start: ` + ( ex as Error ) . message ) ;
370382 }
371- this . _logger . terminal . writeVerboseLine ( `Resolved modulePath is "${ resolvedModulePath } "` ) ;
383+ logger . terminal . writeVerboseLine ( `Resolved modulePath is "${ resolvedModulePath } "` ) ;
372384
373385 // Example: "/path/to/my-project/.storybook"
374386 const dotStorybookFolderPath : string = `${ heftConfiguration . buildFolderPath } /.storybook` ;
@@ -390,6 +402,7 @@ export default class StorybookPlugin implements IHeftTaskPlugin<IStorybookPlugin
390402 } ) ;
391403
392404 return {
405+ ...options ,
393406 workingDirectory : heftConfiguration . buildFolderPath ,
394407 resolvedModulePath,
395408 moduleDefaultArgs,
@@ -402,10 +415,10 @@ export default class StorybookPlugin implements IHeftTaskPlugin<IStorybookPlugin
402415 runStorybookOptions : IRunStorybookOptions ,
403416 options : IStorybookPluginOptions
404417 ) : Promise < void > {
405- const { resolvedModulePath, verbose } = runStorybookOptions ;
418+ const { logger , resolvedModulePath, verbose, isServeMode , isTestMode , isDocsMode } = runStorybookOptions ;
406419 let { workingDirectory, outputFolder } = runStorybookOptions ;
407- this . _logger . terminal . writeLine ( 'Running Storybook compilation' ) ;
408- this . _logger . terminal . writeVerboseLine ( `Loading Storybook module "${ resolvedModulePath } "` ) ;
420+ logger . terminal . writeLine ( 'Running Storybook compilation' ) ;
421+ logger . terminal . writeVerboseLine ( `Loading Storybook module "${ resolvedModulePath } "` ) ;
409422 const storybookCliVersion : `${StorybookCliVersion } ` = this . _getStorybookVersion ( options ) ;
410423
411424 /**
@@ -424,34 +437,42 @@ export default class StorybookPlugin implements IHeftTaskPlugin<IStorybookPlugin
424437 baseFolderPath : workingDirectory
425438 } ) ;
426439
427- this . _logger . terminal . writeVerboseLine ( `Changing Storybook working directory to "${ workingDirectory } "` ) ;
440+ logger . terminal . writeVerboseLine ( `Changing Storybook working directory to "${ workingDirectory } "` ) ;
428441 }
429442
430443 const storybookArgs : string [ ] = runStorybookOptions . moduleDefaultArgs ?? [ ] ;
431444
432445 if ( outputFolder ) {
433446 storybookArgs . push ( '--output-dir' , outputFolder ) ;
434447 }
448+
435449 if ( options . captureWebpackStats ) {
436450 storybookArgs . push ( '--webpack-stats-json' ) ;
437451 }
452+
438453 if ( ! verbose ) {
439454 storybookArgs . push ( '--quiet' ) ;
440455 }
441- if ( this . _isTestMode ) {
456+
457+ if ( isTestMode ) {
442458 storybookArgs . push ( '--test' ) ;
443459 }
444460
445- if ( this . _isServeMode ) {
461+ if ( isDocsMode ) {
462+ storybookArgs . push ( '--docs' ) ;
463+ }
464+
465+ if ( isServeMode ) {
446466 // Instantiate storybook runner synchronously for incremental builds
447467 // this ensure that the process is not killed when heft watcher detects file changes
448468 this . _invokeSync (
469+ logger ,
449470 resolvedModulePath ,
450471 storybookArgs ,
451472 storybookCliVersion === StorybookCliVersion . STORYBOOK8
452473 ) ;
453474 } else {
454- await this . _invokeAsSubprocessAsync ( resolvedModulePath , storybookArgs , workingDirectory ) ;
475+ await this . _invokeAsSubprocessAsync ( logger , resolvedModulePath , storybookArgs , workingDirectory ) ;
455476 }
456477 }
457478
@@ -462,7 +483,12 @@ export default class StorybookPlugin implements IHeftTaskPlugin<IStorybookPlugin
462483 * @param cwd - working directory
463484 * @returns
464485 */
465- private async _invokeAsSubprocessAsync ( command : string , args : string [ ] , cwd : string ) : Promise < void > {
486+ private async _invokeAsSubprocessAsync (
487+ logger : IScopedLogger ,
488+ command : string ,
489+ args : string [ ] ,
490+ cwd : string
491+ ) : Promise < void > {
466492 return await new Promise < void > ( ( resolve , reject ) => {
467493 const storybookEnv : NodeJS . ProcessEnv = { ...process . env } ;
468494 const forkedProcess : child_process . ChildProcess = child_process . fork ( command , args , {
@@ -479,12 +505,12 @@ export default class StorybookPlugin implements IHeftTaskPlugin<IStorybookPlugin
479505 if ( childPid === undefined ) {
480506 throw new InternalError ( `Failed to spawn child process` ) ;
481507 }
482- this . _logger . terminal . writeVerboseLine ( `Started storybook process #${ childPid } ` ) ;
508+ logger . terminal . writeVerboseLine ( `Started storybook process #${ childPid } ` ) ;
483509
484510 // Apply the pipe here instead of doing it in the forked process args due to a bug in Node
485511 // We will output stderr to the normal stdout stream since all output is piped through
486512 // stdout. We have to rely on the exit code to determine if there was an error.
487- const terminal : ITerminal = this . _logger . terminal ;
513+ const terminal : ITerminal = logger . terminal ;
488514 const terminalOutStream : TerminalStreamWritable = new TerminalStreamWritable ( {
489515 terminal,
490516 severity : TerminalProviderSeverity . log
@@ -520,8 +546,13 @@ export default class StorybookPlugin implements IHeftTaskPlugin<IStorybookPlugin
520546 * @param args - storybook args
521547 * @param cwd - working directory
522548 */
523- private _invokeSync ( command : string , args : string [ ] , patchNpmConfigUserAgent : boolean ) : void {
524- this . _logger . terminal . writeLine ( 'Launching ' + command ) ;
549+ private _invokeSync (
550+ logger : IScopedLogger ,
551+ command : string ,
552+ args : string [ ] ,
553+ patchNpmConfigUserAgent : boolean
554+ ) : void {
555+ logger . terminal . writeLine ( 'Launching ' + command ) ;
525556
526557 // simulate storybook cli command
527558 const originalArgv : string [ ] = process . argv ;
@@ -547,7 +578,7 @@ export default class StorybookPlugin implements IHeftTaskPlugin<IStorybookPlugin
547578 // restore original heft process argv
548579 process . argv = originalArgv ;
549580
550- this . _logger . terminal . writeVerboseLine ( 'Completed synchronous portion of launching startupModulePath' ) ;
581+ logger . terminal . writeVerboseLine ( 'Completed synchronous portion of launching startupModulePath' ) ;
551582 }
552583
553584 private _getStorybookVersion ( options : IStorybookPluginOptions ) : `${StorybookCliVersion } ` {
0 commit comments