90 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
		
		
			
		
	
	
			90 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
|  | /** | ||
|  |  * @license | ||
|  |  * Copyright Google LLC 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
 | ||
|  |  */ | ||
|  | const EXTRACT_GENERATED_TRANSLATIONS_REGEXP = | ||
|  |     /const\s*(.*?)\s*=\s*goog\.getMsg\("(.*?)",?\s*(.*?)\)/g; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Verify that placeholders in translation strings match placeholders in the object defined in the | ||
|  |  * `goog.getMsg()` function arguments. | ||
|  |  */ | ||
|  | export function verifyPlaceholdersIntegrity(output: string): boolean { | ||
|  |   const translations = extractTranslations(output); | ||
|  |   translations.forEach(([msg, args]) => { | ||
|  |     const bodyPhs = extractPlaceholdersFromMsg(msg); | ||
|  |     const argsPhs = extractPlaceholdersFromArgs(args); | ||
|  |     if (bodyPhs.size !== argsPhs.size || diff(bodyPhs, argsPhs).size) { | ||
|  |       return false; | ||
|  |     } | ||
|  |   }); | ||
|  |   return true; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Verify that all the variables initialized with `goog.getMsg()` calls have | ||
|  |  * unique names. | ||
|  |  */ | ||
|  | export function verifyUniqueConsts(output: string): boolean { | ||
|  |   extract( | ||
|  |       output, EXTRACT_GENERATED_TRANSLATIONS_REGEXP, | ||
|  |       (current: string[], state: Set<string>): string => { | ||
|  |         const key = current[1]; | ||
|  |         if (state.has(key)) { | ||
|  |           throw new Error(`Duplicate const ${key} found in generated output!`); | ||
|  |         } | ||
|  |         return key; | ||
|  |       }); | ||
|  |   return true; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /** | ||
|  |  * Extract pairs of `[msg, placeholders]`, in calls to `goog.getMsg()`, from the `source`. | ||
|  |  * | ||
|  |  * @param source The source code to parse. | ||
|  |  */ | ||
|  | function extractTranslations(source: string): Set<string[]> { | ||
|  |   return extract( | ||
|  |       source, EXTRACT_GENERATED_TRANSLATIONS_REGEXP, | ||
|  |       ([, , msg, placeholders]) => [msg, placeholders]); | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Extract placeholder names (of the form `{$PLACEHOLDER}`) from the `msg`. | ||
|  |  * | ||
|  |  * @param msg The text of the message to parse. | ||
|  |  */ | ||
|  | function extractPlaceholdersFromMsg(msg: string): Set<string> { | ||
|  |   const regex = /{\$(.*?)}/g; | ||
|  |   return extract(msg, regex, ([, placeholders]) => placeholders); | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Extract the placeholder names (of the form `"PLACEHOLDER": "XXX"`) from the body of the argument | ||
|  |  * provided as `args`. | ||
|  |  * | ||
|  |  * @param args The body of an object literal containing placeholder info. | ||
|  |  */ | ||
|  | function extractPlaceholdersFromArgs(args: string): Set<string> { | ||
|  |   const regex = /\s+"(.+?)":\s*".*?"/g; | ||
|  |   return extract(args, regex, ([, placeholders]) => placeholders); | ||
|  | } | ||
|  | 
 | ||
|  | function extract<T>( | ||
|  |     from: string, regex: RegExp, transformFn: (match: string[], state: Set<T>) => T): Set<T> { | ||
|  |   const result = new Set<T>(); | ||
|  |   let item; | ||
|  |   while ((item = regex.exec(from)) !== null) { | ||
|  |     result.add(transformFn(item, result)); | ||
|  |   } | ||
|  |   return result; | ||
|  | } | ||
|  | 
 | ||
|  | function diff(a: Set<string>, b: Set<string>): Set<string> { | ||
|  |   return new Set(Array.from(a).filter(x => !b.has(x))); | ||
|  | } |