Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
U
utils
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
EIT-SWE
ux
utils
Commits
888e0f1f
Commit
888e0f1f
authored
May 20, 2020
by
Ryan Diehl
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'feature/analytics' into 'develop'
feat(analytics): ported from @psu/analytics See merge request
!29
parents
24d7e8c2
66ac6738
Pipeline
#101514
passed with stages
in 5 minutes and 27 seconds
Changes
18
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
463 additions
and
1 deletion
+463
-1
angular.json
angular.json
+28
-0
libs/utils/analytics/README.md
libs/utils/analytics/README.md
+20
-0
libs/utils/analytics/jest.config.js
libs/utils/analytics/jest.config.js
+9
-0
libs/utils/analytics/ng-package.json
libs/utils/analytics/ng-package.json
+5
-0
libs/utils/analytics/src/index.ts
libs/utils/analytics/src/index.ts
+5
-0
libs/utils/analytics/src/lib/analytics.model.ts
libs/utils/analytics/src/lib/analytics.model.ts
+13
-0
libs/utils/analytics/src/lib/analytics.service.ts
libs/utils/analytics/src/lib/analytics.service.ts
+24
-0
libs/utils/analytics/src/lib/google-analytics.service.ts
libs/utils/analytics/src/lib/google-analytics.service.ts
+85
-0
libs/utils/analytics/src/lib/google-tag-manager/google-tag-manager.service.ts
.../src/lib/google-tag-manager/google-tag-manager.service.ts
+51
-0
libs/utils/analytics/src/lib/interceptor/error.interceptor.spec.ts
...s/analytics/src/lib/interceptor/error.interceptor.spec.ts
+123
-0
libs/utils/analytics/src/lib/interceptor/error.interceptor.ts
.../utils/analytics/src/lib/interceptor/error.interceptor.ts
+49
-0
libs/utils/analytics/src/test-setup.ts
libs/utils/analytics/src/test-setup.ts
+2
-0
libs/utils/analytics/tsconfig.json
libs/utils/analytics/tsconfig.json
+7
-0
libs/utils/analytics/tsconfig.lib.json
libs/utils/analytics/tsconfig.lib.json
+20
-0
libs/utils/analytics/tsconfig.spec.json
libs/utils/analytics/tsconfig.spec.json
+10
-0
libs/utils/analytics/tslint.json
libs/utils/analytics/tslint.json
+7
-0
nx.json
nx.json
+3
-0
tsconfig.json
tsconfig.json
+2
-1
No files found.
angular.json
View file @
888e0f1f
...
...
@@ -448,6 +448,34 @@
"styleext"
:
"scss"
}
}
},
"utils-analytics"
:
{
"projectType"
:
"library"
,
"root"
:
"libs/utils/analytics"
,
"sourceRoot"
:
"libs/utils/analytics/src"
,
"prefix"
:
"ut"
,
"architect"
:
{
"lint"
:
{
"builder"
:
"@angular-devkit/build-angular:tslint"
,
"options"
:
{
"tsConfig"
:
[
"libs/utils/analytics/tsconfig.lib.json"
,
"libs/utils/analytics/tsconfig.spec.json"
],
"exclude"
:
[
"**/node_modules/**"
,
"!libs/utils/analytics/**"
]
}
},
"test"
:
{
"builder"
:
"@nrwl/jest:jest"
,
"options"
:
{
"jestConfig"
:
"libs/utils/analytics/jest.config.js"
,
"tsConfig"
:
"libs/utils/analytics/tsconfig.spec.json"
,
"setupFile"
:
"libs/utils/analytics/src/test-setup.ts"
}
}
},
"schematics"
:
{
"@nrwl/angular:component"
:
{
"styleext"
:
"scss"
}
}
}
},
"cli"
:
{
...
...
libs/utils/analytics/README.md
0 → 100644
View file @
888e0f1f
# @psu/utils/analytics
Provides integration with Google Analytics
## Setup
1.
Setup Google Tag Manager and generate a container ID for your app (usually non-prod + prod)
1.
Provide
`ANALYTICS_CONFIG`
injection token and initialize it in your app initializer
1.
Inject
`GoogleTagManagerService`
into your
`AppComponent`
## ErrorInterceptor
There is an optional
`ErrorInterceptor`
HTTP interceptor that can log any type of network errors to Google Analytics.
There is a custom report/dashboard that you can then view to see a summary of errors.
This has been helpful to debug specific customer problems - CORS errors, anti-virus software
that disabled PUT requests, etc.
## Custom Events
`AnalyticsService`
can record page views and custom analytics events.
libs/utils/analytics/jest.config.js
0 → 100644
View file @
888e0f1f
module
.
exports
=
{
name
:
'
utils-analytics
'
,
preset
:
'
../../../jest.config.js
'
,
coverageDirectory
:
'
../../../coverage/libs/utils/analytics
'
,
snapshotSerializers
:
[
'
jest-preset-angular/AngularSnapshotSerializer.js
'
,
'
jest-preset-angular/HTMLCommentSerializer.js
'
]
};
libs/utils/analytics/ng-package.json
0 → 100644
View file @
888e0f1f
{
"lib"
:
{
"entryFile"
:
"src/index.ts"
}
}
libs/utils/analytics/src/index.ts
0 → 100644
View file @
888e0f1f
export
*
from
'
./lib/analytics.model
'
;
export
{
AnalyticsService
}
from
'
./lib/analytics.service
'
;
export
{
GoogleAnalyticsService
}
from
'
./lib/google-analytics.service
'
;
export
*
from
'
./lib/google-tag-manager/google-tag-manager.service
'
;
export
{
ErrorStatusInterceptor
}
from
'
./lib/interceptor/error.interceptor
'
;
libs/utils/analytics/src/lib/analytics.model.ts
0 → 100644
View file @
888e0f1f
import
{
InjectionToken
}
from
'
@angular/core
'
;
export
interface
AnalyticsSource
{
pageView
(
url
:
string
):
void
;
recordEvent
(
category
:
string
,
label
:
string
,
action
:
string
,
value
:
any
):
void
;
}
export
interface
AnalyticsConfig
{
trackingId
?:
string
;
tagManagerContainerId
?:
string
;
}
export
const
ANALYTICS_CONFIG
=
new
InjectionToken
<
AnalyticsConfig
>
(
'
psu.utils.analytics.config
'
);
libs/utils/analytics/src/lib/analytics.service.ts
0 → 100644
View file @
888e0f1f
import
{
Injectable
}
from
'
@angular/core
'
;
import
{
NavigationEnd
,
Router
}
from
'
@angular/router
'
;
import
{
filter
,
tap
}
from
'
rxjs/operators
'
;
import
{
GoogleAnalyticsService
}
from
'
./google-analytics.service
'
;
@
Injectable
({
providedIn
:
'
root
'
})
export
class
AnalyticsService
{
constructor
(
private
router
:
Router
,
private
google
:
GoogleAnalyticsService
)
{
this
.
router
.
events
.
pipe
(
filter
(
event
=>
event
instanceof
NavigationEnd
),
tap
(
event
=>
this
.
google
.
pageView
((
event
as
NavigationEnd
).
urlAfterRedirects
))
)
.
subscribe
(
event
=>
{});
}
public
recordEvent
(
action
:
string
,
category
?:
string
,
label
?:
string
):
void
{
this
.
google
.
recordEvent
(
action
,
category
,
label
);
}
public
recordError
(
exceptionMsg
:
string
):
void
{
this
.
google
.
recordException
(
exceptionMsg
);
}
}
libs/utils/analytics/src/lib/google-analytics.service.ts
0 → 100644
View file @
888e0f1f
import
{
isPlatformBrowser
}
from
'
@angular/common
'
;
import
{
Inject
,
Injectable
,
PLATFORM_ID
}
from
'
@angular/core
'
;
import
{
AnalyticsConfig
,
AnalyticsSource
,
ANALYTICS_CONFIG
}
from
'
./analytics.model
'
;
declare
global
{
interface
Window
{
dataLayer
:
unknown
[];
gtag
:
Function
;
}
}
@
Injectable
({
providedIn
:
'
root
'
})
export
class
GoogleAnalyticsService
implements
AnalyticsSource
{
private
gtag
:
Function
;
private
scriptLoaded
=
false
;
constructor
(
@
Inject
(
PLATFORM_ID
)
private
platform
:
Object
,
@
Inject
(
ANALYTICS_CONFIG
)
private
config
:
AnalyticsConfig
)
{
this
.
gtag
=
this
.
loadGoogleAnalytics
();
this
.
gtag
(
'
config
'
,
this
.
config
.
trackingId
,
{
send_page_view
:
false
});
}
private
getReady
():
void
{
if
(
!
this
.
scriptLoaded
)
{
this
.
scriptLoaded
=
true
;
if
(
typeof
window
!==
'
undefined
'
)
{
window
.
dataLayer
=
window
.
dataLayer
||
[];
window
.
gtag
=
function
():
void
{
window
.
dataLayer
.
push
(
arguments
);
};
window
.
gtag
(
'
js
'
,
new
Date
());
}
const
doc
=
<
HTMLDivElement
>
document
.
body
;
const
script
=
document
.
createElement
(
'
script
'
);
script
.
innerHTML
=
''
;
script
.
src
=
`https://www.googletagmanager.com/gtag/js?id=
${
this
.
config
.
trackingId
}
`
;
script
.
async
=
true
;
script
.
defer
=
true
;
doc
.
appendChild
(
script
);
}
}
public
pageView
(
url
:
string
):
void
{
this
.
gtag
(
'
config
'
,
this
.
config
.
trackingId
,
{
page_path
:
url
});
}
public
recordEvent
(
action
:
string
,
category
?:
string
,
label
?:
string
):
void
{
this
.
gtag
(
'
event
'
,
action
,
{
event_category
:
category
,
event_label
:
label
});
}
public
recordException
(
exceptionMsg
:
string
):
void
{
this
.
gtag
(
'
event
'
,
'
exception
'
,
{
method
:
'
Angular
'
,
event_category
:
'
exception
'
,
event_label
:
'
Network Error in From User
'
,
description
:
exceptionMsg
});
}
private
loadGoogleAnalytics
():
Function
{
let
ret
:
Function
;
if
(
isPlatformBrowser
(
this
.
platform
))
{
this
.
getReady
();
ret
=
(
<
any
>
window
).
gtag
;
if
(
!
ret
)
{
console
.
warn
(
'
Unable to find Google Analytics function (window.gtag). Returning empty function.
'
);
ret
=
()
=>
{};
}
}
else
{
ret
=
()
=>
{};
}
return
ret
;
}
}
libs/utils/analytics/src/lib/google-tag-manager/google-tag-manager.service.ts
0 → 100644
View file @
888e0f1f
import
{
isPlatformBrowser
}
from
'
@angular/common
'
;
import
{
Inject
,
Injectable
,
PLATFORM_ID
}
from
'
@angular/core
'
;
import
{
Logger
}
from
'
@psu/utils/logger
'
;
import
{
AnalyticsConfig
,
ANALYTICS_CONFIG
}
from
'
../analytics.model
'
;
@
Injectable
({
providedIn
:
'
root
'
})
export
class
GoogleTagManagerService
{
private
scriptLoaded
=
false
;
constructor
(
@
Inject
(
PLATFORM_ID
)
private
platform
:
Object
,
@
Inject
(
ANALYTICS_CONFIG
)
private
config
:
AnalyticsConfig
,
private
logger
:
Logger
)
{
this
.
loadGoogleTagManager
();
}
private
loadGoogleTagManager
():
void
{
if
(
isPlatformBrowser
(
this
.
platform
))
{
if
(
!
this
.
scriptLoaded
)
{
if
(
this
.
config
.
tagManagerContainerId
)
{
this
.
scriptLoaded
=
true
;
try
{
const
head
=
document
.
head
;
const
script
=
document
.
createElement
(
'
script
'
);
script
.
innerHTML
=
`(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','
${
this
.
config
.
tagManagerContainerId
}
');`
;
head
.
appendChild
(
script
);
const
doc
=
document
.
body
;
const
noscript
=
document
.
createElement
(
'
noscript
'
);
noscript
.
innerHTML
=
`<iframe src="https://www.googletagmanager.com/ns.html?id=
${
this
.
config
.
tagManagerContainerId
}
"height="0" width="0" style="display:none;visibility:hidden"></iframe>`
;
doc
.
appendChild
(
script
);
}
catch
(
err
)
{
this
.
scriptLoaded
=
false
;
this
.
logger
.
error
(
`Error loading GoogleTagManager scripts:
${
err
}
`
);
}
}
else
{
this
.
logger
.
error
(
`You have injected the GoogleTagManagerService but it did not find tagManagerContainerId in
the ANALYTICS_CONFIG InjectionToken. Possible race condition?`
);
}
}
}
}
}
libs/utils/analytics/src/lib/interceptor/error.interceptor.spec.ts
0 → 100644
View file @
888e0f1f
import
{
HttpClient
,
HttpHeaders
,
HTTP_INTERCEPTORS
}
from
'
@angular/common/http
'
;
import
{
HttpClientTestingModule
,
HttpTestingController
}
from
'
@angular/common/http/testing
'
;
import
{
TestBed
}
from
'
@angular/core/testing
'
;
import
{
NavigationEnd
,
Router
}
from
'
@angular/router
'
;
import
{
RouterTestingModule
}
from
'
@angular/router/testing
'
;
import
{
of
}
from
'
rxjs
'
;
import
{
catchError
}
from
'
rxjs/operators
'
;
import
{
Mock
}
from
'
ts-mocks
'
;
import
{
AnalyticsConfig
,
ANALYTICS_CONFIG
}
from
'
../..
'
;
import
{
AnalyticsService
}
from
'
../analytics.service
'
;
import
{
ErrorStatusInterceptor
}
from
'
./error.interceptor
'
;
describe
(
'
ErrorStatusInterceptor
'
,
()
=>
{
let
gtag
:
Mock
<
AnalyticsService
>
;
let
mockRouter
:
Mock
<
Router
>
;
const
config
:
AnalyticsConfig
=
{};
const
end
:
NavigationEnd
=
{
urlAfterRedirects
:
''
,
id
:
1
,
url
:
''
};
let
backend
:
HttpTestingController
;
let
http
:
HttpClient
;
beforeEach
(()
=>
{
gtag
=
new
Mock
<
AnalyticsService
>
({
recordError
:
()
=>
{}
});
mockRouter
=
new
Mock
<
Router
>
({
events
:
of
(
end
)
});
TestBed
.
configureTestingModule
({
imports
:
[
RouterTestingModule
,
HttpClientTestingModule
],
providers
:
[
{
provide
:
Router
,
useValue
:
mockRouter
.
Object
},
{
provide
:
AnalyticsService
,
useValue
:
gtag
.
Object
},
{
provide
:
ANALYTICS_CONFIG
,
useFactory
:
()
=>
config
},
{
provide
:
HTTP_INTERCEPTORS
,
useClass
:
ErrorStatusInterceptor
,
multi
:
true
}
]
});
backend
=
TestBed
.
get
(
HttpTestingController
);
http
=
TestBed
.
get
(
HttpClient
);
});
afterEach
(()
=>
{
backend
.
verify
();
});
it
(
'
should create
'
,
()
=>
{
expect
(
TestBed
.
get
(
HTTP_INTERCEPTORS
)[
0
]).
toBeDefined
();
});
describe
(
'
intercepting http calls
'
,
()
=>
{
function
makeRequest
(
method
=
'
GET
'
,
url
=
'
/data
'
,
headers
?:
HttpHeaders
)
{
http
.
request
(
method
,
url
,
{
headers
})
.
pipe
(
catchError
(
err
=>
of
(
null
)))
.
subscribe
();
}
describe
(
'
when request has no headers
'
,
()
=>
{
beforeEach
(()
=>
{
makeRequest
();
});
describe
(
'
when http response is error
'
,
()
=>
{
beforeEach
(()
=>
{
backend
.
expectOne
(
'
/data
'
).
flush
({
errorMessage
:
'
Uh oh!
'
},
{
status
:
500
,
statusText
:
'
Server Error
'
});
});
it
(
'
should call recordError with no request ID
'
,
()
=>
{
expect
(
gtag
.
Object
.
recordError
).
toHaveBeenCalledWith
(
jasmine
.
stringMatching
(
/^
((?!
xRequestId
)
.
)
*$/
));
expect
(
gtag
.
Object
.
recordError
).
toHaveBeenCalledWith
(
jasmine
.
stringMatching
(
/^HTTP 500: Server Error/
));
expect
(
gtag
.
Object
.
recordError
).
toHaveBeenCalledWith
(
jasmine
.
stringMatching
(
/requestMethod: GET/
));
expect
(
gtag
.
Object
.
recordError
).
toHaveBeenCalledWith
(
jasmine
.
stringMatching
(
/fullRequestUrl:
\/
data/
));
expect
(
gtag
.
Object
.
recordError
).
toHaveBeenCalledWith
(
jasmine
.
stringMatching
(
/Uh oh!/
));
});
});
describe
(
'
when http error response has request ID header
'
,
()
=>
{
beforeEach
(()
=>
{
backend
.
expectOne
(
'
/data
'
)
.
flush
({},
{
status
:
500
,
statusText
:
'
No
'
,
headers
:
{
'
X-Request-Id
'
:
'
server-request
'
}
});
});
it
(
'
should record event with request id from server
'
,
()
=>
{
expect
(
gtag
.
Object
.
recordError
).
toHaveBeenCalledWith
(
jasmine
.
stringMatching
(
/xRequestId: server-request/
));
});
});
});
describe
(
'
when request has requestid header
'
,
()
=>
{
beforeEach
(()
=>
{
makeRequest
(
'
PUT
'
,
'
/other/data?id=1
'
,
new
HttpHeaders
().
append
(
'
X-Request-Id
'
,
'
client-request
'
));
});
it
(
'
should record event with request id from client
'
,
()
=>
{
backend
.
expectOne
(
'
/other/data?id=1
'
)
.
flush
({
errorMessage
:
'
Uh oh!
'
},
{
status
:
404
,
statusText
:
'
Not Found
'
});
expect
(
gtag
.
Object
.
recordError
).
toHaveBeenCalledWith
(
jasmine
.
stringMatching
(
/xRequestId: client-request/
));
expect
(
gtag
.
Object
.
recordError
).
toHaveBeenCalledWith
(
jasmine
.
stringMatching
(
/requestMethod: PUT/
));
expect
(
gtag
.
Object
.
recordError
).
toHaveBeenCalledWith
(
jasmine
.
stringMatching
(
/fullRequestUrl:
\/
other
\/
data
\?
id=1/
)
);
});
it
(
'
should log client errors differently
'
,
()
=>
{
backend
.
expectOne
(
'
/other/data?id=1
'
).
error
(
new
ErrorEvent
(
'
CORS error
'
,
{
message
:
'
not allowed
'
}));
expect
(
gtag
.
Object
.
recordError
).
toHaveBeenCalledWith
(
jasmine
.
stringMatching
(
/xRequestId: client-request/
));
expect
(
gtag
.
Object
.
recordError
).
toHaveBeenCalledWith
(
jasmine
.
stringMatching
(
/Client
\/
Network Error: CORS error -> not allowed/
)
);
});
});
});
});
libs/utils/analytics/src/lib/interceptor/error.interceptor.ts
0 → 100644
View file @
888e0f1f
import
{
HttpErrorResponse
,
HttpEvent
,
HttpHandler
,
HttpInterceptor
,
HttpRequest
}
from
'
@angular/common/http
'
;
import
{
Injectable
}
from
'
@angular/core
'
;
import
{
Observable
,
throwError
}
from
'
rxjs
'
;
import
{
catchError
}
from
'
rxjs/operators
'
;
import
{
AnalyticsService
}
from
'
../analytics.service
'
;
const
X_REQUEST_ID
=
'
x-request-id
'
;
@
Injectable
({
providedIn
:
'
root
'
})
export
class
ErrorStatusInterceptor
implements
HttpInterceptor
{
constructor
(
private
aService
:
AnalyticsService
)
{}
public
intercept
(
request
:
HttpRequest
<
any
>
,
next
:
HttpHandler
):
Observable
<
HttpEvent
<
any
>>
{
return
next
.
handle
(
request
).
pipe
(
catchError
((
error
:
HttpErrorResponse
)
=>
{
let
errorMsg
=
'
not provided
'
;
// capture the request ID either from the error response or the original request
let
xRequestId
=
''
;
if
(
error
.
headers
.
has
(
X_REQUEST_ID
))
{
xRequestId
=
error
.
headers
.
get
(
X_REQUEST_ID
);
}
else
if
(
request
.
headers
.
has
(
X_REQUEST_ID
))
{
xRequestId
=
request
.
headers
.
get
(
X_REQUEST_ID
);
}
if
(
error
.
error
&&
error
.
error
.
errorMessage
)
{
errorMsg
=
error
.
error
.
errorMessage
;
}
// there are 2 possible types of errors: client side and server side HTTP errors
// CORS errors fall into client side errors, they will have ErrorCode 0 in chrome and you will not see the real response code.
if
(
error
.
error
instanceof
ErrorEvent
)
{
// client error
errorMsg
=
`Client/Network Error:
${
error
.
error
.
type
}
->
${
error
.
error
.
message
}
`
;
}
else
{
// server error, pull out HTTP response status and message
errorMsg
=
`HTTP
${
error
.
status
}
:
${
error
.
statusText
}
, message: [
${
errorMsg
}
]`
;
}
this
.
aService
.
recordError
(
`
${
errorMsg
}
time: [
${
new
Date
().
toISOString
()}
] from [
${
error
.
url
}
] requestMethod:
${
request
.
method
}
fullRequestUrl:
${
request
.
urlWithParams
}
${
!!
xRequestId
?
`xRequestId:
${
xRequestId
}
`
:
''
}
`
);
return
throwError
(
error
);
})
);
}
}
libs/utils/analytics/src/test-setup.ts
0 → 100644
View file @
888e0f1f
import
'
jest-preset-angular
'
;
import
'
../../../../jestGlobalMocks
'
;
libs/utils/analytics/tsconfig.json
0 → 100644
View file @
888e0f1f
{
"extends"
:
"../../../tsconfig.json"
,
"compilerOptions"
:
{
"types"
:
[
"node"
,
"jest"
]
},
"include"
:
[
"**/*.ts"
]
}
libs/utils/analytics/tsconfig.lib.json
0 → 100644
View file @
888e0f1f
{
"extends"
:
"./tsconfig.json"
,
"compilerOptions"
:
{
"outDir"
:
"../../../dist/out-tsc"
,
"target"
:
"es2015"
,
"declaration"
:
true
,
"inlineSources"
:
true
,
"types"
:
[],
"lib"
:
[
"dom"
,
"es2018"
]
},
"angularCompilerOptions"
:
{
"annotateForClosureCompiler"
:
true
,
"skipTemplateCodegen"
:
true
,
"strictMetadataEmit"
:
true
,
"fullTemplateTypeCheck"
:
true
,
"strictInjectionParameters"
:
true
,
"enableResourceInlining"
:
true
},
"exclude"
:
[
"src/test-setup.ts"
,
"**/*.spec.ts"
]
}
libs/utils/analytics/tsconfig.spec.json
0 → 100644
View file @
888e0f1f
{
"extends"
:
"./tsconfig.json"
,
"compilerOptions"
:
{
"outDir"
:
"../../../dist/out-tsc"
,
"module"
:
"commonjs"
,
"types"
:
[
"jest"
,
"node"
]
},
"files"
:
[
"src/test-setup.ts"
],
"include"
:
[
"**/*.spec.ts"
,
"**/*.d.ts"
]
}
libs/utils/analytics/tslint.json
0 → 100644
View file @
888e0f1f
{
"extends"
:
"../../../tslint.json"
,
"rules"
:
{
"directive-selector"
:
[
true
,
"attribute"
,
"ut"
,
"camelCase"
],
"component-selector"
:
[
true
,
"element"
,
"ut"
,
"kebab-case"
]
}
}
nx.json
View file @
888e0f1f
...
...
@@ -52,6 +52,9 @@
},
"utils-form"
:
{
"tags"
:
[]
},
"utils-analytics"
:
{
"tags"
:
[]
}
}
}
tsconfig.json
View file @
888e0f1f
...
...
@@ -28,7 +28,8 @@
"@psu/utils/loading-events"
:
[
"libs/utils/loading-events/src/index.ts"
],
"@psu/utils/ngrx"
:
[
"libs/utils/ngrx/src/index.ts"
],
"@psu/utils/cdn"
:
[
"libs/utils/cdn/src/index.ts"
],
"@psu/utils/form"
:
[
"libs/utils/form/src/index.ts"
]
"@psu/utils/form"
:
[
"libs/utils/form/src/index.ts"
],
"@psu/utils/analytics"
:
[
"libs/utils/analytics/src/index.ts"
]
}
},
"exclude"
:
[
"node_modules"
,
"tmp"
]
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment