import angular from 'angular' ; import { $injector } from 'ngimport' ; function isAnnotatedArray ( x ) { return ( Array . isArray ( x ) && x . slice ( 0 , - 1 ) . every ( ( v ) => typeof v === 'string' ) && typeof x . slice ( - 1 ) [ 0 ] === 'function' ) ; } function isAnnotatedFunction ( x ) { return typeof x === 'function' && Array . isArray ( x . $inject ) && x . $inject . every ( ( e ) => typeof e === 'string' ) ; } function isNoArg ( x ) { return typeof x === 'function' && x . length === 0 ; } function assertAnnotatedFunction ( injectedFn , message ) { if ( ! ( isAnnotatedArray ( injectedFn ) || isAnnotatedFunction ( injectedFn ) || isNoArg ( injectedFn ) ) ) { [ ... arguments ] . forEach ( ( arg , i ) => console . log ( i , arg ) ) ; throw new Error ( `StrictDI: ${ message }: Expected this function to be explicitly annotated for AngularJS` ) ; } } function invokeAnnotatedFunction ( name , angularModuleFn , argumentIdx ) { return function ( ) { assertAnnotatedFunction ( arguments [ argumentIdx ] , `angular.module().${ name }()` ) ; return angularModuleFn . apply ( this , arguments ) ; } ; } function validateDirectiveDefinitionObject ( ddo ) { const { controller } = ddo ; if ( controller && ! isAnnotatedArray ( controller ) && ! isAnnotatedFunction ( controller ) && ! isNoArg ( controller ) && typeof controller !== 'string' ) { throw new Error ( `StrictDI: angular.module().(directive|component).controller: Expected controller to be annotated for AngularJS` ) ; } } const angularJSModuleStrictDiHandler = { get : function ( angularModule , fnName , _receiver ) { const angularModuleFn = angularModule [ fnName ] ; switch ( fnName ) { case 'config' : case 'run' : return invokeAnnotatedFunction ( fnName , angularModuleFn , 0 ) ; case 'animation' : case 'controller' : case 'factory' : case 'filter' : case 'provider' : case 'service' : return invokeAnnotatedFunction ( fnName , angularModuleFn , 1 ) ; case 'component' : return function ( ) { const [ name , componentObject ] = arguments ; if ( componentObject . controller && typeof componentObject . controller !== 'string' ) { assertAnnotatedFunction ( componentObject . controller , `angular.module().component('${ name }', componentObject.controller)` ) ; } return angularModuleFn . apply ( this , arguments ) ; } ; case 'directive' : return function ( ) { const [ name , ddoFactory , ... rest ] = arguments ; assertAnnotatedFunction ( ddoFactory , `angular.module().directive('${ name }', ddoFactory)` ) ; function wrappedFactory ( ) { const ddo = $injector . invoke ( ddoFactory ) ; validateDirectiveDefinitionObject ( ddo ) ; return ddo ; } return angularModuleFn . apply ( this , [ name , wrappedFactory , ... rest ] ) ; } ; } return angularModule [ fnName ] ; } , } ; const allowlistedModules = [ 'ngMock' ] ; const realModule = angular . module ; ( angular as any ) . module = function module ( ) { const angularModule = realModule . apply ( this , arguments ) ; const isAllowlisted = allowlistedModules . includes ( arguments [ 0 ] ) ; return isAllowlisted ? angularModule : new Proxy ( angularModule , angularJSModuleStrictDiHandler ) ; } ;