| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							|  |  |  |  * Copyright Google Inc. All Rights Reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Use of this source code is governed by an MIT-style license that can be | 
					
						
							|  |  |  |  * found in the LICENSE file at https://angular.io/license
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // tslint:disable:no-console
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TODO(alexeagle): why not import chalk from 'chalk'?
 | 
					
						
							|  |  |  | // Something to do with TS default export in UMD emit...
 | 
					
						
							|  |  |  | const chalk = require('chalk'); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  | import * as minimist from 'minimist'; | 
					
						
							|  |  |  | import * as path from 'path'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-25 13:05:49 -08:00
										 |  |  | import {SerializationOptions, generateGoldenFile, verifyAgainstGoldenFile, discoverAllEntrypoints} from './main'; | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-17 11:30:36 +01:00
										 |  |  | /** Name of the CLI */ | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  | const CMD = 'ts-api-guardian'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-08 17:25:54 -07:00
										 |  |  | /** Name of the Bazel workspace that runs the CLI. */ | 
					
						
							|  |  |  | const bazelWorkspaceName = process.env.BAZEL_WORKSPACE; | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Path to the Bazel workspace directory. Only set if the CLI is run with `bazel run`. | 
					
						
							|  |  |  |  * https://docs.bazel.build/versions/master/user-manual.html#run.
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | const bazelWorkspaceDirectory = process.env.BUILD_WORKSPACE_DIRECTORY; | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Regular expression that matches Bazel manifest paths that start with the | 
					
						
							|  |  |  |  * current Bazel workspace, followed by a path delimiter. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | const bazelWorkspaceManifestPathRegex = | 
					
						
							|  |  |  |     bazelWorkspaceName ? new RegExp(`^${bazelWorkspaceName}[/\\\\]`) : null; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  | export function startCli() { | 
					
						
							|  |  |  |   const {argv, mode, errors} = parseArguments(process.argv.slice(2)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const options: SerializationOptions = { | 
					
						
							| 
									
										
										
										
											2018-09-19 13:40:21 +02:00
										 |  |  |     stripExportPattern: [].concat(argv['stripExportPattern']), | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |     allowModuleIdentifiers: [].concat(argv['allowModuleIdentifiers']), | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-28 19:05:20 +01:00
										 |  |  |   // Since the API guardian can be also used by other projects, we should not set up the default
 | 
					
						
							|  |  |  |   // Angular project tag rules unless specified explicitly through a given option.
 | 
					
						
							|  |  |  |   if (argv['useAngularTagRules']) { | 
					
						
							|  |  |  |     options.exportTags = { | 
					
						
							| 
									
										
										
										
											2019-04-10 13:45:26 -07:00
										 |  |  |       requireAtLeastOne: ['publicApi', 'codeGenApi'], | 
					
						
							| 
									
										
										
										
											2018-10-28 19:05:20 +01:00
										 |  |  |       banned: ['experimental'], | 
					
						
							|  |  |  |       toCopy: ['deprecated'] | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     options.memberTags = { | 
					
						
							| 
									
										
										
										
											2019-04-10 13:45:26 -07:00
										 |  |  |       requireAtLeastOne: [], | 
					
						
							|  |  |  |       banned: ['experimental', 'publicApi', 'codeGenApi'], | 
					
						
							| 
									
										
										
										
											2018-10-28 19:05:20 +01:00
										 |  |  |       toCopy: ['deprecated'] | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     options.paramTags = { | 
					
						
							| 
									
										
										
										
											2019-04-10 13:45:26 -07:00
										 |  |  |       requireAtLeastOne: [], | 
					
						
							|  |  |  |       banned: ['experimental', 'publicApi', 'codeGenApi'], | 
					
						
							| 
									
										
										
										
											2018-10-28 19:05:20 +01:00
										 |  |  |       toCopy: ['deprecated'] | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-25 13:05:49 -08:00
										 |  |  |   // In autoDiscoverEntrypoints mode we set the inputed files as the discovered entrypoints
 | 
					
						
							|  |  |  |   // for the rootDir
 | 
					
						
							|  |  |  |   let entrypoints: string[]; | 
					
						
							|  |  |  |   if (argv['autoDiscoverEntrypoints']) { | 
					
						
							|  |  |  |     entrypoints = discoverAllEntrypoints(argv['rootDir']); | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     entrypoints = argv._.slice(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   for (const error of errors) { | 
					
						
							|  |  |  |     console.warn(error); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (mode === 'help') { | 
					
						
							|  |  |  |     printUsageAndExit(!!errors.length); | 
					
						
							|  |  |  |   } else { | 
					
						
							| 
									
										
										
										
											2020-02-25 13:05:49 -08:00
										 |  |  |     const targets = resolveFileNamePairs(argv, mode, entrypoints); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (mode === 'out') { | 
					
						
							|  |  |  |       for (const {entrypoint, goldenFile} of targets) { | 
					
						
							|  |  |  |         generateGoldenFile(entrypoint, goldenFile, options); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } else {  // mode === 'verify'
 | 
					
						
							|  |  |  |       let hasDiff = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       for (const {entrypoint, goldenFile} of targets) { | 
					
						
							|  |  |  |         const diff = verifyAgainstGoldenFile(entrypoint, goldenFile, options); | 
					
						
							|  |  |  |         if (diff) { | 
					
						
							|  |  |  |           hasDiff = true; | 
					
						
							|  |  |  |           const lines = diff.split('\n'); | 
					
						
							|  |  |  |           if (lines.length) { | 
					
						
							|  |  |  |             lines.pop();  // Remove trailing newline
 | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           for (const line of lines) { | 
					
						
							| 
									
										
										
										
											2020-04-08 17:37:04 -07:00
										 |  |  |             const chalkMap: | 
					
						
							|  |  |  |                 {[key: string]: any} = {'-': chalk.red, '+': chalk.green, '@': chalk.cyan}; | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |             const chalkFunc = chalkMap[line[0]] || chalk.reset; | 
					
						
							|  |  |  |             console.log(chalkFunc(line)); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (hasDiff) { | 
					
						
							| 
									
										
										
										
											2020-01-16 12:15:06 +01:00
										 |  |  |         const bazelTarget = process.env['BAZEL_TARGET']; | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |         // Under bazel, give instructions how to use bazel run to accept the golden file.
 | 
					
						
							| 
									
										
										
										
											2020-01-16 12:15:06 +01:00
										 |  |  |         if (bazelTarget) { | 
					
						
							| 
									
										
										
										
											2019-11-18 10:57:16 -08:00
										 |  |  |           console.error('\n\nIf you modify a public API, you must accept the new golden file.'); | 
					
						
							|  |  |  |           console.error('\n\nTo do so, execute the following Bazel target:'); | 
					
						
							| 
									
										
										
										
											2020-04-08 17:37:04 -07:00
										 |  |  |           console.error(`  yarn bazel run ${bazelTarget.replace(/_bin$/, '')}.accept`); | 
					
						
							| 
									
										
										
										
											2020-01-16 12:15:06 +01:00
										 |  |  |           if (process.env['TEST_WORKSPACE'] === 'angular') { | 
					
						
							|  |  |  |             console.error('\n\nFor more information, see'); | 
					
						
							|  |  |  |             console.error( | 
					
						
							|  |  |  |                 '\n  https://github.com/angular/angular/blob/master/docs/PUBLIC_API.md#golden-files'); | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-01-16 12:15:06 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |         process.exit(1); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function parseArguments(input: string[]): | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |     {argv: minimist.ParsedArgs, mode: string, errors: string[]} { | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   let help = false; | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |   const errors: string[] = []; | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   const argv = minimist(input, { | 
					
						
							|  |  |  |     string: [ | 
					
						
							|  |  |  |       'out', 'outDir', 'verify', 'verifyDir', 'rootDir', 'stripExportPattern', | 
					
						
							| 
									
										
										
										
											2018-04-04 21:31:34 +01:00
										 |  |  |       'allowModuleIdentifiers' | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |     ], | 
					
						
							|  |  |  |     boolean: [ | 
					
						
							| 
									
										
										
										
											2020-02-25 13:05:49 -08:00
										 |  |  |       'help', 'useAngularTagRules', 'autoDiscoverEntrypoints', | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |       // Options used by chalk automagically
 | 
					
						
							|  |  |  |       'color', 'no-color' | 
					
						
							|  |  |  |     ], | 
					
						
							|  |  |  |     alias: {'outFile': 'out', 'verifyFile': 'verify'}, | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |     unknown: (option: string) => { | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |       if (option[0] === '-') { | 
					
						
							|  |  |  |         errors.push(`Unknown option: ${option}`); | 
					
						
							|  |  |  |         help = true; | 
					
						
							|  |  |  |         return false;  // do not add to argv._
 | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         return true;  // add to argv._
 | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   help = help || argv['help']; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (help) { | 
					
						
							|  |  |  |     return {argv, mode: 'help', errors}; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   let modes: string[] = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (argv['out']) { | 
					
						
							|  |  |  |     modes.push('out'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (argv['outDir']) { | 
					
						
							|  |  |  |     modes.push('out'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (argv['verify']) { | 
					
						
							|  |  |  |     modes.push('verify'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (argv['verifyDir']) { | 
					
						
							|  |  |  |     modes.push('verify'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-25 13:05:49 -08:00
										 |  |  |   if (argv['autoDiscoverEntrypoints']) { | 
					
						
							|  |  |  |     if (!argv['rootDir']) { | 
					
						
							|  |  |  |       errors.push(`--rootDir must be provided with --autoDiscoverEntrypoints.`); | 
					
						
							|  |  |  |       modes = ['help']; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (!argv['outDir'] && !argv['verifyDir']) { | 
					
						
							|  |  |  |       errors.push(`--outDir or --verifyDir must be used with --autoDiscoverEntrypoints.`); | 
					
						
							|  |  |  |       modes = ['help']; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     if (!argv._.length) { | 
					
						
							|  |  |  |       errors.push('No input file specified.'); | 
					
						
							|  |  |  |       modes = ['help']; | 
					
						
							|  |  |  |     } else if (modes.length !== 1) { | 
					
						
							|  |  |  |       errors.push('Specify either --out[Dir] or --verify[Dir]'); | 
					
						
							|  |  |  |       modes = ['help']; | 
					
						
							|  |  |  |     } else if (argv._.length > 1 && !argv['outDir'] && !argv['verifyDir']) { | 
					
						
							|  |  |  |       errors.push(`More than one input specified. Use --${modes[0]}Dir instead.`); | 
					
						
							|  |  |  |       modes = ['help']; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return {argv, mode: modes[0], errors}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function printUsageAndExit(error = false) { | 
					
						
							|  |  |  |   const print = error ? console.warn.bind(console) : console.log.bind(console); | 
					
						
							|  |  |  |   print(`Usage:  ${CMD} [options] <file ...>
 | 
					
						
							|  |  |  |         ${CMD} --out <output file> <entrypoint .d.ts file> | 
					
						
							|  |  |  |         ${CMD} --outDir <output dir> [--rootDir .] <entrypoint .d.ts files> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         ${CMD} --verify <golden file> <entrypoint .d.ts file> | 
					
						
							|  |  |  |         ${CMD} --verifyDir <golden file dir> [--rootDir .] <entrypoint .d.ts files> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Options: | 
					
						
							|  |  |  |         --help                          Show this usage message | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         --out <file>                    Write golden output to file | 
					
						
							|  |  |  |         --outDir <dir>                  Write golden file structure to directory | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         --verify <file>                 Read golden input from file | 
					
						
							|  |  |  |         --verifyDir <dir>               Read golden file structure from directory | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         --rootDir <dir>                 Specify the root directory of input files | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-29 11:20:17 -07:00
										 |  |  |         --useAngularTagRules <boolean>  Whether the Angular specific tag rules should be used. | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |         --stripExportPattern <regexp>   Do not output exports matching the pattern | 
					
						
							|  |  |  |         --allowModuleIdentifiers <identifier> | 
					
						
							| 
									
										
										
										
											2020-02-25 13:05:49 -08:00
										 |  |  |                                         Allow identifier for "* as foo" imports | 
					
						
							|  |  |  |         --autoDiscoverEntrypoints       Automatically find all entrypoints .d.ts files in the rootDir`);
 | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   process.exit(error ? 1 : 0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-29 21:25:00 +01:00
										 |  |  | /** | 
					
						
							| 
									
										
										
										
											2020-03-17 11:30:36 +01:00
										 |  |  |  * Resolves a given path in the file system. If `ts-api-guardian` runs with Bazel, file paths | 
					
						
							|  |  |  |  * are resolved through runfiles. Additionally in Bazel, this method handles the case where | 
					
						
							|  |  |  |  * manifest file paths are not existing, but need to resolve to the Bazel workspace directory. | 
					
						
							|  |  |  |  * This happens commonly when goldens are approved, but the golden file does not exist yet. | 
					
						
							| 
									
										
										
										
											2018-10-29 21:25:00 +01:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2020-03-17 11:30:36 +01:00
										 |  |  | function resolveFilePath(fileName: string): string { | 
					
						
							|  |  |  |   // If an absolute path is specified, the path is already resolved.
 | 
					
						
							|  |  |  |   if (path.isAbsolute(fileName)) { | 
					
						
							|  |  |  |     return fileName; | 
					
						
							| 
									
										
										
										
											2020-03-17 11:30:36 +01:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-03-17 11:30:36 +01:00
										 |  |  |   // Outside of Bazel, file paths are resolved based on the current working directory.
 | 
					
						
							| 
									
										
										
										
											2020-04-08 17:25:54 -07:00
										 |  |  |   if (!bazelWorkspaceName) { | 
					
						
							| 
									
										
										
										
											2020-03-17 11:30:36 +01:00
										 |  |  |     return path.resolve(fileName); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // In Bazel, we first try to resolve the file through the runfiles. We do this by calling
 | 
					
						
							| 
									
										
										
										
											2020-04-08 17:25:54 -07:00
										 |  |  |   // the `require.resolve` function that is patched by the Bazel NodeJS rules. Note that we
 | 
					
						
							|  |  |  |   // need to catch errors because files inside tree artifacts cannot be resolved through
 | 
					
						
							|  |  |  |   // runfile manifests. Hence, we need to have alternative resolution logic when resolving
 | 
					
						
							|  |  |  |   // file paths. Additionally, it could happen that manifest paths which aren't part of the
 | 
					
						
							| 
									
										
										
										
											2020-03-17 11:30:36 +01:00
										 |  |  |   // runfiles are specified (i.e. golden is approved but does not exist in the workspace yet).
 | 
					
						
							|  |  |  |   try { | 
					
						
							| 
									
										
										
										
											2020-04-08 17:25:54 -07:00
										 |  |  |     return require.resolve(fileName); | 
					
						
							| 
									
										
										
										
											2020-03-17 11:30:36 +01:00
										 |  |  |   } catch { | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // This handles cases where file paths cannot be resolved through runfiles. This happens
 | 
					
						
							|  |  |  |   // commonly when goldens are approved while the golden does not exist in the workspace yet.
 | 
					
						
							|  |  |  |   // In those cases, we want to build up a relative path based on the manifest path, and join
 | 
					
						
							|  |  |  |   // it with the absolute bazel workspace directory (which is only set in `bazel run`).
 | 
					
						
							|  |  |  |   // e.g. `angular/goldens/<..>/common` should become `{workspace_dir}/goldens/<...>/common`.
 | 
					
						
							|  |  |  |   if (bazelWorkspaceManifestPathRegex !== null && bazelWorkspaceDirectory && | 
					
						
							|  |  |  |       bazelWorkspaceManifestPathRegex.test(fileName)) { | 
					
						
							|  |  |  |     return path.join(bazelWorkspaceDirectory, fileName.substr(bazelWorkspaceName.length + 1)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   throw Error(`Could not resolve file path in runfiles: ${fileName}`); | 
					
						
							| 
									
										
										
										
											2018-10-29 21:25:00 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-25 13:05:49 -08:00
										 |  |  | function resolveFileNamePairs(argv: minimist.ParsedArgs, mode: string, entrypoints: string[]): | 
					
						
							|  |  |  |     {entrypoint: string, goldenFile: string}[] { | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   if (argv[mode]) { | 
					
						
							| 
									
										
										
										
											2018-10-29 21:25:00 +01:00
										 |  |  |     return [{ | 
					
						
							| 
									
										
										
										
											2020-03-17 11:30:36 +01:00
										 |  |  |       entrypoint: resolveFilePath(entrypoints[0]), | 
					
						
							|  |  |  |       goldenFile: resolveFilePath(argv[mode]), | 
					
						
							| 
									
										
										
										
											2018-10-29 21:25:00 +01:00
										 |  |  |     }]; | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   } else {  // argv[mode + 'Dir']
 | 
					
						
							|  |  |  |     let rootDir = argv['rootDir'] || '.'; | 
					
						
							|  |  |  |     const goldenDir = argv[mode + 'Dir']; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-25 13:05:49 -08:00
										 |  |  |     return entrypoints.map((fileName: string) => { | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |       return { | 
					
						
							| 
									
										
										
										
											2020-03-17 11:30:36 +01:00
										 |  |  |         entrypoint: resolveFilePath(fileName), | 
					
						
							|  |  |  |         goldenFile: resolveFilePath(path.join(goldenDir, path.relative(rootDir, fileName))), | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |       }; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } |