docs(core): analytics setup (#5894)

This commit is contained in:
Benjamin Cabanes 2021-06-07 09:10:04 -04:00 committed by GitHub
parent b4d27fcfd7
commit 20719ced3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 431 additions and 29 deletions

View File

@ -26,5 +26,6 @@ module.exports = {
'<rootDir>/nx-dev/data-access-documents', '<rootDir>/nx-dev/data-access-documents',
'<rootDir>/nx-dev/data-access-menu', '<rootDir>/nx-dev/data-access-menu',
'<rootDir>/nx-dev/feature-search', '<rootDir>/nx-dev/feature-search',
'<rootDir>/nx-dev/feature-analytics',
], ],
}; };

View File

@ -0,0 +1,12 @@
{
"presets": [
[
"@nrwl/react/babel",
{
"runtime": "automatic",
"useBuiltIns": "usage"
}
]
],
"plugins": []
}

View File

@ -0,0 +1,21 @@
{
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"parserOptions": {
"project": ["nx-dev/feature-analytics/tsconfig.*?.json"]
},
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}

View File

@ -0,0 +1,7 @@
# nx-dev-feature-analytics
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test nx-dev-feature-analytics` to execute the unit tests via [Jest](https://jestjs.io).

View File

@ -0,0 +1,9 @@
module.exports = {
displayName: 'nx-dev-feature-analytics',
preset: '../../jest.preset.js',
transform: {
'^.+\\.[tj]sx?$': 'babel-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/nx-dev/feature-analytics',
};

View File

@ -0,0 +1 @@
export * from './lib/google-analytics';

View File

@ -0,0 +1,40 @@
/**
* [Gtag sending data documentation](https://developers.google.com/analytics/devguides/collection/gtagjs/sending-data)
*
* [About Events](https://support.google.com/analytics/answer/1033068/about-events)
*/
import { Gtag } from './gtag';
declare const gtag: Gtag;
export function sendPageViewEvent(data: {
gaId: string;
path?: string;
title?: string;
}): void {
try {
gtag('config', data.gaId, {
...(!!data.path && { page_path: data.path }),
...(!!data.title && { page_title: data.title }),
});
} catch (exception) {
throw new Error(`Cannot send Google Tag event: ${exception}`);
}
}
export function sendCustomEvent(
action: string,
category: string,
label: string,
value?: number
): void {
try {
gtag('event', action, {
event_category: category,
event_label: label,
value,
});
} catch (error) {
throw new Error(`Cannot send Google Tag event: ${error}`);
}
}

View File

@ -0,0 +1,123 @@
export interface Gtag {
(
command: 'config',
targetId: string,
config?: ConfigParams | ControlParams | EventParams | CustomParams
): void;
(command: 'set', targetId: string, config: CustomParams | boolean): void;
(command: 'set', config: CustomParams): void;
(command: 'js', config: Date): void;
(
command: 'event',
eventName: EventNames | string,
eventParams?: ControlParams | EventParams | CustomParams
): void;
(
command: 'get',
targetId: string,
fieldName: FieldNames | string,
callback?: (field: string) => any
): void;
(
command: 'consent',
consentArg: ConsentArg | string,
consentParams: ConsentParams
): void;
}
interface CustomParams {
[key: string]: any;
}
interface ConfigParams {
page_location?: string;
page_path?: string;
page_title?: string;
send_page_view?: boolean;
}
interface ControlParams {
groups?: string | string[];
send_to?: string | string[];
event_callback?: () => void;
event_timeout?: number;
}
type EventNames =
| 'add_payment_info'
| 'add_to_cart'
| 'add_to_wishlist'
| 'begin_checkout'
| 'checkout_progress'
| 'exception'
| 'generate_lead'
| 'login'
| 'page_view'
| 'purchase'
| 'refund'
| 'remove_from_cart'
| 'screen_view'
| 'search'
| 'select_content'
| 'set_checkout_option'
| 'share'
| 'sign_up'
| 'timing_complete'
| 'view_item'
| 'view_item_list'
| 'view_promotion'
| 'view_search_results';
interface EventParams {
checkout_option?: string;
checkout_step?: number;
content_id?: string;
content_type?: string;
coupon?: string;
currency?: string;
description?: string;
fatal?: boolean;
items?: Item[];
method?: string;
number?: string;
promotions?: Promotion[];
screen_name?: string;
search_term?: string;
shipping?: Currency;
tax?: Currency;
transaction_id?: string;
value?: number;
event_label?: string;
event_category?: string;
}
type Currency = string | number;
interface Item {
brand?: string;
category?: string;
creative_name?: string;
creative_slot?: string;
id?: string;
location_id?: string;
name?: string;
price?: Currency;
quantity?: number;
}
interface Promotion {
creative_name?: string;
creative_slot?: string;
id?: string;
name?: string;
}
type FieldNames = 'client_id' | 'session_id' | 'gclid';
type ConsentArg = 'default' | 'update';
interface ConsentParams {
ad_storage?: 'granted' | 'denied';
analytics_storage?: 'granted' | 'denied';
wait_for_update?: number;
region?: string[];
}

View File

@ -0,0 +1,19 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"jsx": "react-jsx",
"allowJs": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}

View File

@ -0,0 +1,13 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": ["node"]
},
"files": [
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
"../../node_modules/@nrwl/react/typings/image.d.ts"
],
"exclude": ["**/*.spec.ts", "**/*.spec.tsx"],
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
}

View File

@ -0,0 +1,15 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node"]
},
"include": [
"**/*.spec.ts",
"**/*.spec.tsx",
"**/*.spec.js",
"**/*.spec.jsx",
"**/*.d.ts"
]
}

View File

@ -5,11 +5,13 @@ import { CopyToClipboard } from 'react-copy-to-clipboard';
export function CodeBlock({ export function CodeBlock({
text, text,
language, language,
callback,
...rest ...rest
}: { }: {
text: string; text: string;
language: string; language: string;
[key: string]: any; [key: string]: any;
callback: (text: string) => void;
}) { }) {
const [copied, setCopied] = useState(false); const [copied, setCopied] = useState(false);
useEffect(() => { useEffect(() => {
@ -25,7 +27,13 @@ export function CodeBlock({
}, [copied]); }, [copied]);
return ( return (
<div className="relative group"> <div className="relative group">
<CopyToClipboard text={text} onCopy={() => setCopied(true)}> <CopyToClipboard
text={text}
onCopy={() => {
setCopied(true);
callback(text);
}}
>
<button className="flex absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity"> <button className="flex absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"

View File

@ -4,7 +4,7 @@ import autolinkHeadings from 'rehype-autolink-headings';
import gfm from 'remark-gfm'; import gfm from 'remark-gfm';
import slug from 'rehype-slug'; import slug from 'rehype-slug';
import { DocumentData } from '@nrwl/nx-dev/data-access-documents'; import { DocumentData } from '@nrwl/nx-dev/data-access-documents';
import { sendCustomEvent } from '@nrwl/nx-dev/feature-analytics';
import { transformLinkPath } from './renderers/transform-link-path'; import { transformLinkPath } from './renderers/transform-link-path';
import { transformImagePath } from './renderers/transform-image-path'; import { transformImagePath } from './renderers/transform-image-path';
import { renderIframes } from './renderers/render-iframe'; import { renderIframes } from './renderers/render-iframe';
@ -16,7 +16,10 @@ export interface ContentProps {
version: string; version: string;
} }
const components: any = { interface ComponentsConfig {
readonly code: { callback: (command: string) => void };
}
const components: any = (config: ComponentsConfig) => ({
code({ node, inline, className, children, ...props }) { code({ node, inline, className, children, ...props }) {
const language = /language-(\w+)/.exec(className || '')?.[1]; const language = /language-(\w+)/.exec(className || '')?.[1];
return !inline && language ? ( return !inline && language ? (
@ -24,6 +27,7 @@ const components: any = {
text={String(children).replace(/\n$/, '')} text={String(children).replace(/\n$/, '')}
language={language} language={language}
{...props} {...props}
callback={(command) => config.code.callback(command)}
/> />
) : ( ) : (
<code className={className} {...props}> <code className={className} {...props}>
@ -34,7 +38,7 @@ const components: any = {
pre({ children }) { pre({ children }) {
return <>{children}</>; return <>{children}</>;
}, },
}; });
export function Content(props: ContentProps) { export function Content(props: ContentProps) {
return ( return (
@ -52,7 +56,16 @@ export function Content(props: ContentProps) {
document: props.document, document: props.document,
})} })}
className="prose max-w-none" className="prose max-w-none"
components={components} components={components({
code: {
callback: () =>
sendCustomEvent(
'code-snippets',
'click',
props.document.filePath
),
},
})}
/> />
</div> </div>
); );

View File

@ -5,7 +5,7 @@
{ {
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"], "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"parserOptions": { "parserOptions": {
"project": ["/nx-dev/feature-search/tsconfig.*?.json"] "project": ["nx-dev/feature-search/tsconfig.*?.json"]
}, },
"rules": {} "rules": {}
}, },

View File

@ -1,4 +1,4 @@
import { useState, useCallback, useRef, useEffect } from 'react'; import { useCallback, useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
import Link from 'next/link'; import Link from 'next/link';
import Head from 'next/head'; import Head from 'next/head';

View File

@ -1,9 +1,19 @@
import React from 'react'; import React, { useEffect } from 'react';
import { AppProps } from 'next/app'; import { AppProps } from 'next/app';
import Head from 'next/head'; import Head from 'next/head';
import { useRouter } from 'next/router';
import { sendPageViewEvent } from '@nrwl/nx-dev/feature-analytics';
import '../styles/main.css'; import '../styles/main.css';
export default function CustomApp({ Component, pageProps }: AppProps) { export default function CustomApp({ Component, pageProps }: AppProps) {
const router = useRouter();
const gaMeasurementId = 'UA-88380372-10';
useEffect(() => {
const handleRouteChange = (url: URL) =>
sendPageViewEvent(gaMeasurementId, { path: url });
router.events.on('routeChangeStart', (url) => handleRouteChange(url));
return () => router.events.off('routeChangeStart', handleRouteChange);
}, [router]);
return ( return (
<> <>
<Head> <Head>
@ -34,6 +44,23 @@ export default function CustomApp({ Component, pageProps }: AppProps) {
<meta name="msapplication-TileColor" content="#da532c" /> <meta name="msapplication-TileColor" content="#da532c" />
<meta name="theme-color" content="#ffffff" /> <meta name="theme-color" content="#ffffff" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
{/* Global Site Tag (gtag.js) - Google Analytics */}
<script
async
src={`https://www.googletagmanager.com/gtag/js?id=${gaMeasurementId}`}
/>
<script
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){ dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', '${gaMeasurementId}', {
page_path: window.location.pathname,
});
`,
}}
/>
</Head> </Head>
<div className="documentation-app text-gray-700 antialiased bg-white"> <div className="documentation-app text-gray-700 antialiased bg-white">
<Component {...pageProps} /> <Component {...pageProps} />

View File

@ -1,3 +1,5 @@
import React from 'react';
import { useRouter } from 'next/router';
import Image from 'next/image'; import Image from 'next/image';
import Link from 'next/link'; import Link from 'next/link';
import { import {
@ -6,10 +8,11 @@ import {
InlineCommand, InlineCommand,
NxUsersShowcase, NxUsersShowcase,
} from '@nrwl/nx-dev/ui/common'; } from '@nrwl/nx-dev/ui/common';
import React from 'react'; import { sendCustomEvent } from '@nrwl/nx-dev/feature-analytics';
import { useStorage } from '../lib/use-storage'; import { useStorage } from '../lib/use-storage';
export function AngularPage() { export function AngularPage() {
const router = useRouter();
const { value: storedFlavor } = useStorage('flavor'); const { value: storedFlavor } = useStorage('flavor');
const { value: storedVersion } = useStorage('version'); const { value: storedVersion } = useStorage('version');
return ( return (
@ -116,6 +119,9 @@ export function AngularPage() {
<InlineCommand <InlineCommand
language={'bash'} language={'bash'}
command={'npx create-nx-workspace --preset=angular'} command={'npx create-nx-workspace --preset=angular'}
callback={() =>
sendCustomEvent('code-snippets', 'click', router.pathname)
}
/> />
</div> </div>
</div> </div>

View File

@ -1,4 +1,6 @@
import React from 'react'; import React from 'react';
import { useRouter } from 'next/router';
import Image from 'next/image';
import Link from 'next/link'; import Link from 'next/link';
import { import {
Footer, Footer,
@ -6,10 +8,11 @@ import {
InlineCommand, InlineCommand,
NxUsersShowcase, NxUsersShowcase,
} from '@nrwl/nx-dev/ui/common'; } from '@nrwl/nx-dev/ui/common';
import Image from 'next/image'; import { sendCustomEvent } from '@nrwl/nx-dev/feature-analytics';
import { useStorage } from '../lib/use-storage'; import { useStorage } from '../lib/use-storage';
export function Index() { export function Index() {
const router = useRouter();
const { value: storedFlavor } = useStorage('flavor'); const { value: storedFlavor } = useStorage('flavor');
const { value: storedVersion } = useStorage('version'); const { value: storedVersion } = useStorage('version');
return ( return (
@ -50,6 +53,13 @@ export function Index() {
<InlineCommand <InlineCommand
language={'bash'} language={'bash'}
command={'npx create-nx-workspace'} command={'npx create-nx-workspace'}
callback={() =>
sendCustomEvent(
'code-snippets',
'click',
router.pathname
)
}
/> />
</div> </div>
</div> </div>

View File

@ -1,3 +1,5 @@
import React from 'react';
import { useRouter } from 'next/router';
import Image from 'next/image'; import Image from 'next/image';
import Link from 'next/link'; import Link from 'next/link';
import { import {
@ -6,7 +8,7 @@ import {
InlineCommand, InlineCommand,
NxUsersShowcase, NxUsersShowcase,
} from '@nrwl/nx-dev/ui/common'; } from '@nrwl/nx-dev/ui/common';
import React from 'react'; import { sendCustomEvent } from '@nrwl/nx-dev/feature-analytics';
import { useStorage } from '../lib/use-storage'; import { useStorage } from '../lib/use-storage';
export function Node() { export function Node() {
@ -52,6 +54,7 @@ export function Node() {
].join(' '), ].join(' '),
}, },
]; ];
const router = useRouter();
const { value: storedFlavor } = useStorage('flavor'); const { value: storedFlavor } = useStorage('flavor');
const { value: storedVersion } = useStorage('version'); const { value: storedVersion } = useStorage('version');
@ -232,6 +235,9 @@ export function Node() {
<InlineCommand <InlineCommand
language={'bash'} language={'bash'}
command={'npx create-nx-workspace --preset=nest'} command={'npx create-nx-workspace --preset=nest'}
callback={() =>
sendCustomEvent('code-snippets', 'click', router.pathname)
}
/> />
</div> </div>
@ -243,6 +249,9 @@ export function Node() {
<InlineCommand <InlineCommand
language={'bash'} language={'bash'}
command={'npx create-nx-workspace --preset=express'} command={'npx create-nx-workspace --preset=express'}
callback={() =>
sendCustomEvent('code-snippets', 'click', router.pathname)
}
/> />
</div> </div>
</div> </div>

View File

@ -1,3 +1,5 @@
import React from 'react';
import { useRouter } from 'next/router';
import Image from 'next/image'; import Image from 'next/image';
import Link from 'next/link'; import Link from 'next/link';
import { import {
@ -6,10 +8,11 @@ import {
InlineCommand, InlineCommand,
NxUsersShowcase, NxUsersShowcase,
} from '@nrwl/nx-dev/ui/common'; } from '@nrwl/nx-dev/ui/common';
import React from 'react'; import { sendCustomEvent } from '@nrwl/nx-dev/feature-analytics';
import { useStorage } from '../lib/use-storage'; import { useStorage } from '../lib/use-storage';
export function ReactPage() { export function ReactPage() {
const router = useRouter();
const { value: storedFlavor } = useStorage('flavor'); const { value: storedFlavor } = useStorage('flavor');
const { value: storedVersion } = useStorage('version'); const { value: storedVersion } = useStorage('version');
return ( return (
@ -107,6 +110,9 @@ export function ReactPage() {
<InlineCommand <InlineCommand
language={'bash'} language={'bash'}
command={'npx create-nx-workspace --preset=react'} command={'npx create-nx-workspace --preset=react'}
callback={() =>
sendCustomEvent('code-snippets', 'click', router.pathname)
}
/> />
</div> </div>
@ -118,6 +124,9 @@ export function ReactPage() {
<InlineCommand <InlineCommand
language={'bash'} language={'bash'}
command={'npx create-nx-workspace --preset=next'} command={'npx create-nx-workspace --preset=next'}
callback={() =>
sendCustomEvent('code-snippets', 'click', router.pathname)
}
/> />
</div> </div>
</div> </div>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -6,9 +6,14 @@ import { CopyToClipboard } from 'react-copy-to-clipboard';
export interface InlineCommandProps { export interface InlineCommandProps {
language: string; language: string;
command: string; command: string;
callback?: (command: string) => void;
} }
export function InlineCommand({ language, command }: InlineCommandProps) { export function InlineCommand({
language,
command,
callback,
}: InlineCommandProps) {
const [copied, setCopied] = useState(false); const [copied, setCopied] = useState(false);
useEffect(() => { useEffect(() => {
let t: NodeJS.Timeout; let t: NodeJS.Timeout;
@ -23,7 +28,13 @@ export function InlineCommand({ language, command }: InlineCommandProps) {
}, [copied]); }, [copied]);
return ( return (
<div className="relative"> <div className="relative">
<CopyToClipboard text={command} onCopy={() => setCopied(true)}> <CopyToClipboard
text={command}
onCopy={() => {
setCopied(true);
if (typeof callback === 'function') callback(command);
}}
>
<button <button
type="button" type="button"
className="max-w-full text-sm flex-none bg-white text-gray-400 hover:text-gray-900 font-mono leading-6 py-1 sm:px-3 border border-gray-200 rounded-xl flex items-center justify-center space-x-2 sm:space-x-4 focus:ring-2 focus:ring-offset-2 focus:ring-offset-white focus:ring-gray-300 focus:outline-none transition-colors duration-180" className="max-w-full text-sm flex-none bg-white text-gray-400 hover:text-gray-900 font-mono leading-6 py-1 sm:px-3 border border-gray-200 rounded-xl flex items-center justify-center space-x-2 sm:space-x-4 focus:ring-2 focus:ring-offset-2 focus:ring-offset-white focus:ring-gray-300 focus:outline-none transition-colors duration-180"

View File

@ -1,4 +1,4 @@
import { useState, useEffect } from 'react'; import { useEffect, useState } from 'react';
declare const window: any; declare const window: any;

View File

@ -1,4 +1,4 @@
import React, { Fragment, useState } from 'react'; import React, { Fragment } from 'react';
import { Listbox, Transition } from '@headlessui/react'; import { Listbox, Transition } from '@headlessui/react';
import { CheckIcon, SelectorIcon } from '@heroicons/react/solid'; import { CheckIcon, SelectorIcon } from '@heroicons/react/solid';

27
nx.json
View File

@ -1,9 +1,12 @@
{ {
"npmScope": "nrwl",
"implicitDependencies": { "implicitDependencies": {
"package.json": "*", "package.json": "*",
".eslintrc.json": "*" ".eslintrc.json": "*"
}, },
"affected": {
"defaultBase": "master"
},
"npmScope": "nrwl",
"tasksRunnerOptions": { "tasksRunnerOptions": {
"default": { "default": {
"runner": "@nrwl/nx-cloud", "runner": "@nrwl/nx-cloud",
@ -54,7 +57,9 @@
"nest": { "nest": {
"implicitDependencies": ["node", "linter"] "implicitDependencies": ["node", "linter"]
}, },
"linter": { "implicitDependencies": ["eslint-plugin-nx"] }, "linter": {
"implicitDependencies": ["eslint-plugin-nx"]
},
"express": { "express": {
"implicitDependencies": ["node"] "implicitDependencies": ["node"]
}, },
@ -111,7 +116,9 @@
"e2e-workspace": { "e2e-workspace": {
"implicitDependencies": ["create-nx-workspace"] "implicitDependencies": ["create-nx-workspace"]
}, },
"gatsby": { "tags": [] }, "gatsby": {
"tags": []
},
"dep-graph-dep-graph-e2e": { "dep-graph-dep-graph-e2e": {
"implicitDependencies": ["dep-graph-dep-graph"] "implicitDependencies": ["dep-graph-dep-graph"]
}, },
@ -135,8 +142,14 @@
"nx-dev-data-access-documents": { "nx-dev-data-access-documents": {
"tags": ["scope:nx-dev", "type:data-access"] "tags": ["scope:nx-dev", "type:data-access"]
}, },
"nx-dev-feature-search": { "tags": ["scope:nx-dev", "type:feature"] }, "nx-dev-feature-search": {
"docs": { "tags": ["scope:nx-dev"] } "tags": ["scope:nx-dev", "type:feature"]
}, },
"affected": { "defaultBase": "master" } "docs": {
"tags": ["scope:nx-dev"]
},
"nx-dev-feature-analytics": {
"tags": ["scope:nx-dev", "type:feature"]
}
}
} }

View File

@ -52,7 +52,10 @@
"@nrwl/nx-dev/data-access-documents": [ "@nrwl/nx-dev/data-access-documents": [
"./nx-dev/data-access-documents/src/index.ts" "./nx-dev/data-access-documents/src/index.ts"
], ],
"@nrwl/nx-dev/feature-search": ["./nx-dev/feature-search/src/index.ts"] "@nrwl/nx-dev/feature-search": ["./nx-dev/feature-search/src/index.ts"],
"@nrwl/nx-dev/feature-analytics": [
"./nx-dev/feature-analytics/src/index.ts"
]
} }
} }
} }

View File

@ -1,5 +1,15 @@
{ {
"version": 2, "version": 2,
"cli": {
"defaultCollection": "@nrwl/react"
},
"generators": {
"@nrwl/react": {
"application": {
"babel": true
}
}
},
"projects": { "projects": {
"nx": { "nx": {
"root": "packages/nx", "root": "packages/nx",
@ -2374,9 +2384,29 @@
"root": "docs", "root": "docs",
"sourceRoot": "docs", "sourceRoot": "docs",
"projectType": "library" "projectType": "library"
},
"nx-dev-feature-analytics": {
"root": "nx-dev/feature-analytics",
"sourceRoot": "nx-dev/feature-analytics/src",
"projectType": "library",
"targets": {
"lint": {
"executor": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": [
"nx-dev/feature-analytics/**/*.{ts,tsx,js,jsx}"
]
}
},
"test": {
"executor": "@nrwl/jest:jest",
"outputs": ["coverage/nx-dev/feature-analytics"],
"options": {
"jestConfig": "nx-dev/feature-analytics/jest.config.js",
"passWithNoTests": true
}
}
}
} }
},
"cli": {
"defaultCollection": "@nrwl/workspace"
} }
} }