129 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
		
		
			
		
	
	
			129 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
|  | /** | ||
|  |  * @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
 | ||
|  |  */ | ||
|  | 
 | ||
|  | import * as ts from 'typescript'; | ||
|  | 
 | ||
|  | import {METADATA_VERSION, ModuleMetadata} from '../metadata'; | ||
|  | 
 | ||
|  | import {DTS} from './util'; | ||
|  | 
 | ||
|  | export interface MetadataReaderHost { | ||
|  |   getSourceFileMetadata(filePath: string): ModuleMetadata|undefined; | ||
|  |   cacheMetadata?(fileName: string): boolean; | ||
|  |   fileExists(filePath: string): boolean; | ||
|  |   readFile(filePath: string): string; | ||
|  | } | ||
|  | 
 | ||
|  | export interface MetadataReaderCache { | ||
|  |   /** | ||
|  |    * @internal | ||
|  |    */ | ||
|  |   data: Map<string, ModuleMetadata[]|undefined>; | ||
|  | } | ||
|  | 
 | ||
|  | export function createMetadataReaderCache(): MetadataReaderCache { | ||
|  |   const data = new Map<string, ModuleMetadata[]|undefined>(); | ||
|  |   return {data}; | ||
|  | } | ||
|  | 
 | ||
|  | export function readMetadata( | ||
|  |     filePath: string, host: MetadataReaderHost, cache?: MetadataReaderCache): ModuleMetadata[]| | ||
|  |     undefined { | ||
|  |   let metadatas = cache && cache.data.get(filePath); | ||
|  |   if (metadatas) { | ||
|  |     return metadatas; | ||
|  |   } | ||
|  |   if (host.fileExists(filePath)) { | ||
|  |     // If the file doesn't exists then we cannot return metadata for the file.
 | ||
|  |     // This will occur if the user referenced a declared module for which no file
 | ||
|  |     // exists for the module (i.e. jQuery or angularjs).
 | ||
|  |     if (DTS.test(filePath)) { | ||
|  |       metadatas = readMetadataFile(host, filePath); | ||
|  |       if (!metadatas) { | ||
|  |         // If there is a .d.ts file but no metadata file we need to produce a
 | ||
|  |         // metadata from the .d.ts file as metadata files capture reexports
 | ||
|  |         // (starting with v3).
 | ||
|  |         metadatas = [upgradeMetadataWithDtsData( | ||
|  |             host, {'__symbolic': 'module', 'version': 1, 'metadata': {}}, filePath)]; | ||
|  |       } | ||
|  |     } else { | ||
|  |       const metadata = host.getSourceFileMetadata(filePath); | ||
|  |       metadatas = metadata ? [metadata] : []; | ||
|  |     } | ||
|  |   } | ||
|  |   if (cache && (!host.cacheMetadata || host.cacheMetadata(filePath))) { | ||
|  |     cache.data.set(filePath, metadatas); | ||
|  |   } | ||
|  |   return metadatas; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | function readMetadataFile(host: MetadataReaderHost, dtsFilePath: string): ModuleMetadata[]| | ||
|  |     undefined { | ||
|  |   const metadataPath = dtsFilePath.replace(DTS, '.metadata.json'); | ||
|  |   if (!host.fileExists(metadataPath)) { | ||
|  |     return undefined; | ||
|  |   } | ||
|  |   try { | ||
|  |     const metadataOrMetadatas = JSON.parse(host.readFile(metadataPath)); | ||
|  |     const metadatas: ModuleMetadata[] = metadataOrMetadatas ? | ||
|  |         (Array.isArray(metadataOrMetadatas) ? metadataOrMetadatas : [metadataOrMetadatas]) : | ||
|  |         []; | ||
|  |     if (metadatas.length) { | ||
|  |       let maxMetadata = metadatas.reduce((p, c) => p.version > c.version ? p : c); | ||
|  |       if (maxMetadata.version < METADATA_VERSION) { | ||
|  |         metadatas.push(upgradeMetadataWithDtsData(host, maxMetadata, dtsFilePath)); | ||
|  |       } | ||
|  |     } | ||
|  |     return metadatas; | ||
|  |   } catch (e) { | ||
|  |     console.error(`Failed to read JSON file ${metadataPath}`); | ||
|  |     throw e; | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | function upgradeMetadataWithDtsData( | ||
|  |     host: MetadataReaderHost, oldMetadata: ModuleMetadata, dtsFilePath: string): ModuleMetadata { | ||
|  |   // patch v1 to v3 by adding exports and the `extends` clause.
 | ||
|  |   // patch v3 to v4 by adding `interface` symbols for TypeAlias
 | ||
|  |   let newMetadata: ModuleMetadata = { | ||
|  |     '__symbolic': 'module', | ||
|  |     'version': METADATA_VERSION, | ||
|  |     'metadata': {...oldMetadata.metadata}, | ||
|  |   }; | ||
|  |   if (oldMetadata.exports) { | ||
|  |     newMetadata.exports = oldMetadata.exports; | ||
|  |   } | ||
|  |   if (oldMetadata.importAs) { | ||
|  |     newMetadata.importAs = oldMetadata.importAs; | ||
|  |   } | ||
|  |   if (oldMetadata.origins) { | ||
|  |     newMetadata.origins = oldMetadata.origins; | ||
|  |   } | ||
|  |   const dtsMetadata = host.getSourceFileMetadata(dtsFilePath); | ||
|  |   if (dtsMetadata) { | ||
|  |     for (let prop in dtsMetadata.metadata) { | ||
|  |       if (!newMetadata.metadata[prop]) { | ||
|  |         newMetadata.metadata[prop] = dtsMetadata.metadata[prop]; | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     // Only copy exports from exports from metadata prior to version 3.
 | ||
|  |     // Starting with version 3 the collector began collecting exports and
 | ||
|  |     // this should be redundant. Also, with bundler will rewrite the exports
 | ||
|  |     // which will hoist the exports from modules referenced indirectly causing
 | ||
|  |     // the imports to be different than the .d.ts files and using the .d.ts file
 | ||
|  |     // exports would cause the StaticSymbolResolver to redirect symbols to the
 | ||
|  |     // incorrect location.
 | ||
|  |     if ((!oldMetadata.version || oldMetadata.version < 3) && dtsMetadata.exports) { | ||
|  |       newMetadata.exports = dtsMetadata.exports; | ||
|  |     } | ||
|  |   } | ||
|  |   return newMetadata; | ||
|  | } |