Merge pull request #1272 from 0xProject/feature/instant/heap
[instant] Base heap integration
This commit is contained in:
commit
ba41fc9275
@ -12,6 +12,7 @@ import { DEFAULT_STATE, DefaultState, State } from '../redux/reducer';
|
|||||||
import { store, Store } from '../redux/store';
|
import { store, Store } from '../redux/store';
|
||||||
import { fonts } from '../style/fonts';
|
import { fonts } from '../style/fonts';
|
||||||
import { AccountState, AffiliateInfo, AssetMetaData, Network, OrderSource } from '../types';
|
import { AccountState, AffiliateInfo, AssetMetaData, Network, OrderSource } from '../types';
|
||||||
|
import { analytics, disableAnalytics } from '../util/analytics';
|
||||||
import { assetUtils } from '../util/asset';
|
import { assetUtils } from '../util/asset';
|
||||||
import { errorFlasher } from '../util/error_flasher';
|
import { errorFlasher } from '../util/error_flasher';
|
||||||
import { gasPriceEstimator } from '../util/gas_price_estimator';
|
import { gasPriceEstimator } from '../util/gas_price_estimator';
|
||||||
@ -36,6 +37,7 @@ export interface ZeroExInstantProviderOptionalProps {
|
|||||||
additionalAssetMetaDataMap: ObjectMap<AssetMetaData>;
|
additionalAssetMetaDataMap: ObjectMap<AssetMetaData>;
|
||||||
networkId: Network;
|
networkId: Network;
|
||||||
affiliateInfo: AffiliateInfo;
|
affiliateInfo: AffiliateInfo;
|
||||||
|
shouldDisableAnalyticsTracking: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ZeroExInstantProvider extends React.Component<ZeroExInstantProviderProps> {
|
export class ZeroExInstantProvider extends React.Component<ZeroExInstantProviderProps> {
|
||||||
@ -121,6 +123,18 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider
|
|||||||
gasPriceEstimator.getGasInfoAsync();
|
gasPriceEstimator.getGasInfoAsync();
|
||||||
// tslint:disable-next-line:no-floating-promises
|
// tslint:disable-next-line:no-floating-promises
|
||||||
this._flashErrorIfWrongNetwork();
|
this._flashErrorIfWrongNetwork();
|
||||||
|
|
||||||
|
// Analytics
|
||||||
|
disableAnalytics(this.props.shouldDisableAnalyticsTracking || false);
|
||||||
|
analytics.addEventProperties({
|
||||||
|
embeddedHost: window.location.host,
|
||||||
|
embeddedUrl: window.location.href,
|
||||||
|
networkId: state.network,
|
||||||
|
providerName: state.providerState.name,
|
||||||
|
gitSha: process.env.GIT_SHA,
|
||||||
|
npmVersion: process.env.NPM_PACKAGE_VERSION,
|
||||||
|
});
|
||||||
|
analytics.trackInstantOpened();
|
||||||
}
|
}
|
||||||
public componentWillUnmount(): void {
|
public componentWillUnmount(): void {
|
||||||
if (this._accountUpdateHeartbeat) {
|
if (this._accountUpdateHeartbeat) {
|
||||||
|
@ -16,6 +16,7 @@ export const BUY_QUOTE_UPDATE_INTERVAL_TIME_MS = ONE_SECOND_MS * 15;
|
|||||||
export const DEFAULT_GAS_PRICE = GWEI_IN_WEI.mul(6);
|
export const DEFAULT_GAS_PRICE = GWEI_IN_WEI.mul(6);
|
||||||
export const DEFAULT_ESTIMATED_TRANSACTION_TIME_MS = ONE_MINUTE_MS * 2;
|
export const DEFAULT_ESTIMATED_TRANSACTION_TIME_MS = ONE_MINUTE_MS * 2;
|
||||||
export const ETH_GAS_STATION_API_BASE_URL = 'https://ethgasstation.info';
|
export const ETH_GAS_STATION_API_BASE_URL = 'https://ethgasstation.info';
|
||||||
|
export const HEAP_ANALYTICS_ID = process.env.HEAP_ANALYTICS_ID;
|
||||||
export const COINBASE_API_BASE_URL = 'https://api.coinbase.com/v2';
|
export const COINBASE_API_BASE_URL = 'https://api.coinbase.com/v2';
|
||||||
export const PROGRESS_STALL_AT_WIDTH = '95%';
|
export const PROGRESS_STALL_AT_WIDTH = '95%';
|
||||||
export const PROGRESS_FINISH_ANIMATION_TIME_MS = 200;
|
export const PROGRESS_FINISH_ANIMATION_TIME_MS = 200;
|
||||||
@ -48,4 +49,5 @@ export const PROVIDER_TYPE_TO_NAME: { [key in ProviderType]: string } = {
|
|||||||
[ProviderType.Mist]: 'Mist',
|
[ProviderType.Mist]: 'Mist',
|
||||||
[ProviderType.CoinbaseWallet]: 'Coinbase Wallet',
|
[ProviderType.CoinbaseWallet]: 'Coinbase Wallet',
|
||||||
[ProviderType.Parity]: 'Parity',
|
[ProviderType.Parity]: 'Parity',
|
||||||
|
[ProviderType.Fallback]: 'Fallback',
|
||||||
};
|
};
|
||||||
|
@ -35,6 +35,9 @@ export const render = (props: ZeroExInstantOverlayProps, selector: string = DEFA
|
|||||||
if (!_.isUndefined(props.provider)) {
|
if (!_.isUndefined(props.provider)) {
|
||||||
assert.isWeb3Provider('props.provider', props.provider);
|
assert.isWeb3Provider('props.provider', props.provider);
|
||||||
}
|
}
|
||||||
|
if (!_.isUndefined(props.shouldDisableAnalyticsTracking)) {
|
||||||
|
assert.isBoolean('props.shouldDisableAnalyticsTracking', props.shouldDisableAnalyticsTracking);
|
||||||
|
}
|
||||||
assert.isString('selector', selector);
|
assert.isString('selector', selector);
|
||||||
const appendToIfExists = document.querySelector(selector);
|
const appendToIfExists = document.querySelector(selector);
|
||||||
assert.assert(!_.isNull(appendToIfExists), `Could not find div with selector: ${selector}`);
|
assert.assert(!_.isNull(appendToIfExists), `Could not find div with selector: ${selector}`);
|
||||||
|
59
packages/instant/src/redux/analytics_middleware.ts
Normal file
59
packages/instant/src/redux/analytics_middleware.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
import { Middleware } from 'redux';
|
||||||
|
|
||||||
|
import { ETH_DECIMALS } from '../constants';
|
||||||
|
import { Account, AccountState } from '../types';
|
||||||
|
import { analytics } from '../util/analytics';
|
||||||
|
|
||||||
|
import { Action, ActionTypes } from './actions';
|
||||||
|
|
||||||
|
import { State } from './reducer';
|
||||||
|
|
||||||
|
const shouldTriggerWalletReady = (prevAccount: Account, curAccount: Account): boolean => {
|
||||||
|
const didJustTurnReady = curAccount.state === AccountState.Ready && prevAccount.state !== AccountState.Ready;
|
||||||
|
if (didJustTurnReady) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curAccount.state === AccountState.Ready && prevAccount.state === AccountState.Ready) {
|
||||||
|
// Account was ready, and is now ready again, but address has changed
|
||||||
|
return curAccount.address !== prevAccount.address;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const analyticsMiddleware: Middleware = store => next => middlewareAction => {
|
||||||
|
const prevState = store.getState() as State;
|
||||||
|
const prevAccount = prevState.providerState.account;
|
||||||
|
|
||||||
|
const nextAction = next(middlewareAction) as Action;
|
||||||
|
|
||||||
|
const curState = store.getState() as State;
|
||||||
|
const curAccount = curState.providerState.account;
|
||||||
|
|
||||||
|
switch (nextAction.type) {
|
||||||
|
case ActionTypes.SET_ACCOUNT_STATE_READY:
|
||||||
|
if (curAccount.state === AccountState.Ready && shouldTriggerWalletReady(prevAccount, curAccount)) {
|
||||||
|
const ethAddress = curAccount.address;
|
||||||
|
analytics.addUserProperties({ ethAddress });
|
||||||
|
analytics.trackWalletReady();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ActionTypes.UPDATE_ACCOUNT_ETH_BALANCE:
|
||||||
|
if (
|
||||||
|
curAccount.state === AccountState.Ready &&
|
||||||
|
curAccount.ethBalanceInWei &&
|
||||||
|
!_.isEqual(curAccount, prevAccount)
|
||||||
|
) {
|
||||||
|
const ethBalanceInUnitAmount = Web3Wrapper.toUnitAmount(
|
||||||
|
curAccount.ethBalanceInWei,
|
||||||
|
ETH_DECIMALS,
|
||||||
|
).toString();
|
||||||
|
analytics.addUserProperties({ ethBalanceInUnitAmount });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextAction;
|
||||||
|
};
|
@ -1,7 +1,8 @@
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import { createStore, Store as ReduxStore } from 'redux';
|
import { applyMiddleware, createStore, Store as ReduxStore } from 'redux';
|
||||||
import { devToolsEnhancer } from 'redux-devtools-extension/developmentOnly';
|
import { composeWithDevTools } from 'redux-devtools-extension/developmentOnly';
|
||||||
|
|
||||||
|
import { analyticsMiddleware } from './analytics_middleware';
|
||||||
import { createReducer, State } from './reducer';
|
import { createReducer, State } from './reducer';
|
||||||
|
|
||||||
export type Store = ReduxStore<State>;
|
export type Store = ReduxStore<State>;
|
||||||
@ -9,6 +10,6 @@ export type Store = ReduxStore<State>;
|
|||||||
export const store = {
|
export const store = {
|
||||||
create: (initialState: State): Store => {
|
create: (initialState: State): Store => {
|
||||||
const reducer = createReducer(initialState);
|
const reducer = createReducer(initialState);
|
||||||
return createStore(reducer, initialState, devToolsEnhancer({}));
|
return createStore(reducer, initialState, composeWithDevTools(applyMiddleware(analyticsMiddleware)));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -165,4 +165,5 @@ export enum ProviderType {
|
|||||||
Mist = 'MIST',
|
Mist = 'MIST',
|
||||||
CoinbaseWallet = 'COINBASE_WALLET',
|
CoinbaseWallet = 'COINBASE_WALLET',
|
||||||
Cipher = 'CIPHER',
|
Cipher = 'CIPHER',
|
||||||
|
Fallback = 'FALLBACK',
|
||||||
}
|
}
|
||||||
|
64
packages/instant/src/util/analytics.ts
Normal file
64
packages/instant/src/util/analytics.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import { ObjectMap } from '@0x/types';
|
||||||
|
|
||||||
|
import { heapUtil } from './heap';
|
||||||
|
|
||||||
|
let isDisabled = false;
|
||||||
|
export const disableAnalytics = (shouldDisableAnalytics: boolean) => {
|
||||||
|
isDisabled = shouldDisableAnalytics;
|
||||||
|
};
|
||||||
|
export const evaluateIfEnabled = (fnCall: () => void) => {
|
||||||
|
if (isDisabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fnCall();
|
||||||
|
};
|
||||||
|
|
||||||
|
enum EventNames {
|
||||||
|
INSTANT_OPENED = 'Instant - Opened',
|
||||||
|
WALLET_READY = 'Wallet - Ready',
|
||||||
|
}
|
||||||
|
const track = (eventName: EventNames, eventData: ObjectMap<string | number> = {}): void => {
|
||||||
|
evaluateIfEnabled(() => {
|
||||||
|
heapUtil.evaluateHeapCall(heap => heap.track(eventName, eventData));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
function trackingEventFnWithoutPayload(eventName: EventNames): () => void {
|
||||||
|
return () => {
|
||||||
|
track(eventName);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// tslint:disable-next-line:no-unused-variable
|
||||||
|
function trackingEventFnWithPayload<T extends ObjectMap<string | number>>(
|
||||||
|
eventName: EventNames,
|
||||||
|
): (eventDataProperties: T) => void {
|
||||||
|
return (eventDataProperties: T) => {
|
||||||
|
track(eventName, eventDataProperties);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AnalyticsUserOptions {
|
||||||
|
ethAddress?: string;
|
||||||
|
ethBalanceInUnitAmount?: string;
|
||||||
|
}
|
||||||
|
export interface AnalyticsEventOptions {
|
||||||
|
embeddedHost?: string;
|
||||||
|
embeddedUrl?: string;
|
||||||
|
networkId?: number;
|
||||||
|
providerName?: string;
|
||||||
|
gitSha?: string;
|
||||||
|
npmVersion?: string;
|
||||||
|
}
|
||||||
|
export const analytics = {
|
||||||
|
addUserProperties: (properties: AnalyticsUserOptions): void => {
|
||||||
|
evaluateIfEnabled(() => {
|
||||||
|
heapUtil.evaluateHeapCall(heap => heap.addUserProperties(properties));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
addEventProperties: (properties: AnalyticsEventOptions): void => {
|
||||||
|
evaluateIfEnabled(() => {
|
||||||
|
heapUtil.evaluateHeapCall(heap => heap.addEventProperties(properties));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
trackWalletReady: trackingEventFnWithoutPayload(EventNames.WALLET_READY),
|
||||||
|
trackInstantOpened: trackingEventFnWithoutPayload(EventNames.INSTANT_OPENED),
|
||||||
|
};
|
113
packages/instant/src/util/heap.ts
Normal file
113
packages/instant/src/util/heap.ts
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
import { ObjectMap } from '@0x/types';
|
||||||
|
import { logUtils } from '@0x/utils';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { HEAP_ANALYTICS_ID } from '../constants';
|
||||||
|
|
||||||
|
import { AnalyticsEventOptions, AnalyticsUserOptions } from './analytics';
|
||||||
|
|
||||||
|
export interface HeapAnalytics {
|
||||||
|
loaded: boolean;
|
||||||
|
appid: string;
|
||||||
|
identify(id: string, idType: string): void;
|
||||||
|
track(eventName: string, eventProperties?: ObjectMap<string | number>): void;
|
||||||
|
resetIdentity(): void;
|
||||||
|
addUserProperties(properties: AnalyticsUserOptions): void;
|
||||||
|
addEventProperties(properties: AnalyticsEventOptions): void;
|
||||||
|
removeEventProperty(property: string): void;
|
||||||
|
clearEventProperties(): void;
|
||||||
|
}
|
||||||
|
interface ModifiedWindow {
|
||||||
|
heap?: HeapAnalytics;
|
||||||
|
zeroExInstantLoadedHeap?: boolean;
|
||||||
|
}
|
||||||
|
const getWindow = (): ModifiedWindow => {
|
||||||
|
return window as ModifiedWindow;
|
||||||
|
};
|
||||||
|
|
||||||
|
const setupZeroExInstantHeap = () => {
|
||||||
|
if (_.isUndefined(HEAP_ANALYTICS_ID)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const curWindow = getWindow();
|
||||||
|
// Set property to specify that this is zeroEx's heap
|
||||||
|
curWindow.zeroExInstantLoadedHeap = true;
|
||||||
|
|
||||||
|
// Typescript-compatible version of https://docs.heapanalytics.com/docs/installation
|
||||||
|
/* tslint:disable */
|
||||||
|
((window as any).heap = (window as any).heap || []),
|
||||||
|
((window as any).heap.load = function(e: any, t: any) {
|
||||||
|
((window as any).heap.appid = e), ((window as any).heap.config = t = t || {});
|
||||||
|
var r = t.forceSSL || 'https:' === (document.location as Location).protocol,
|
||||||
|
a = document.createElement('script');
|
||||||
|
(a.type = 'text/javascript'),
|
||||||
|
(a.async = !0),
|
||||||
|
(a.src = (r ? 'https:' : 'http:') + '//cdn.heapanalytics.com/js/heap-' + e + '.js');
|
||||||
|
var n = document.getElementsByTagName('script')[0];
|
||||||
|
(n.parentNode as Node).insertBefore(a, n);
|
||||||
|
for (
|
||||||
|
var o = function(e: any) {
|
||||||
|
return function() {
|
||||||
|
(window as any).heap.push([e].concat(Array.prototype.slice.call(arguments, 0)));
|
||||||
|
};
|
||||||
|
},
|
||||||
|
p = [
|
||||||
|
'addEventProperties',
|
||||||
|
'addUserProperties',
|
||||||
|
'clearEventProperties',
|
||||||
|
'identify',
|
||||||
|
'resetIdentity',
|
||||||
|
'removeEventProperty',
|
||||||
|
'setEventProperties',
|
||||||
|
'track',
|
||||||
|
'unsetEventProperty',
|
||||||
|
],
|
||||||
|
c = 0;
|
||||||
|
c < p.length;
|
||||||
|
c++
|
||||||
|
)
|
||||||
|
(window as any).heap[p[c]] = o(p[c]);
|
||||||
|
});
|
||||||
|
(window as any).heap.load(HEAP_ANALYTICS_ID);
|
||||||
|
/* tslint:enable */
|
||||||
|
|
||||||
|
return curWindow.heap as HeapAnalytics;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const heapUtil = {
|
||||||
|
getHeap: (): HeapAnalytics | undefined => {
|
||||||
|
const curWindow = getWindow();
|
||||||
|
const hasOtherExistingHeapIntegration = curWindow.heap && !curWindow.zeroExInstantLoadedHeap;
|
||||||
|
if (hasOtherExistingHeapIntegration) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const zeroExInstantHeapIntegration = curWindow.zeroExInstantLoadedHeap && curWindow.heap;
|
||||||
|
if (zeroExInstantHeapIntegration) {
|
||||||
|
return zeroExInstantHeapIntegration;
|
||||||
|
}
|
||||||
|
|
||||||
|
return setupZeroExInstantHeap();
|
||||||
|
},
|
||||||
|
evaluateHeapCall: (heapFunctionCall: (heap: HeapAnalytics) => void): void => {
|
||||||
|
if (_.isUndefined(HEAP_ANALYTICS_ID)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const curHeap = heapUtil.getHeap();
|
||||||
|
if (curHeap) {
|
||||||
|
try {
|
||||||
|
if (curHeap.appid !== HEAP_ANALYTICS_ID) {
|
||||||
|
// Integrator has included heap after us and reset the app id
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
heapFunctionCall(curHeap);
|
||||||
|
} catch (e) {
|
||||||
|
// We never want analytics to crash our React component
|
||||||
|
// TODO(sk): error reporter here
|
||||||
|
logUtils.log('Analytics error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
@ -56,7 +56,7 @@ export const providerStateFactory = {
|
|||||||
getInitialProviderStateFallback: (orderSource: OrderSource, network: Network): ProviderState => {
|
getInitialProviderStateFallback: (orderSource: OrderSource, network: Network): ProviderState => {
|
||||||
const provider = providerFactory.getFallbackNoSigningProvider(network);
|
const provider = providerFactory.getFallbackNoSigningProvider(network);
|
||||||
const providerState: ProviderState = {
|
const providerState: ProviderState = {
|
||||||
name: envUtil.getProviderName(provider),
|
name: 'Fallback',
|
||||||
provider,
|
provider,
|
||||||
web3Wrapper: new Web3Wrapper(provider),
|
web3Wrapper: new Web3Wrapper(provider),
|
||||||
assetBuyer: assetBuyerFactory.getAssetBuyer(provider, orderSource, network),
|
assetBuyer: assetBuyerFactory.getAssetBuyer(provider, orderSource, network),
|
||||||
|
@ -1,7 +1,31 @@
|
|||||||
const path = require('path');
|
const childProcess = require('child_process');
|
||||||
const ip = require('ip');
|
const ip = require('ip');
|
||||||
|
const path = require('path');
|
||||||
|
const webpack = require('webpack');
|
||||||
|
|
||||||
// The common js bundle (not this one) is built using tsc.
|
// The common js bundle (not this one) is built using tsc.
|
||||||
// The umd bundle (this one) has a different entrypoint.
|
// The umd bundle (this one) has a different entrypoint.
|
||||||
|
|
||||||
|
const GIT_SHA = childProcess
|
||||||
|
.execSync('git rev-parse HEAD')
|
||||||
|
.toString()
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
const HEAP_PRODUCTION_ENV_VAR_NAME = 'INSTANT_HEAP_ANALYTICS_ID_PRODUCTION';
|
||||||
|
const HEAP_DEVELOPMENT_ENV_VAR_NAME = 'INSTANT_HEAP_ANALYTICS_ID_DEVELOPMENT';
|
||||||
|
const getHeapAnalyticsId = modeName => {
|
||||||
|
if (modeName === 'production') {
|
||||||
|
return process.env[HEAP_PRODUCTION_ENV_VAR_NAME];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modeName === 'development') {
|
||||||
|
return process.env[HEAP_DEVELOPMENT_ENV_VAR_NAME];
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = (env, argv) => {
|
||||||
const outputPath = process.env.WEBPACK_OUTPUT_PATH || 'umd';
|
const outputPath = process.env.WEBPACK_OUTPUT_PATH || 'umd';
|
||||||
const config = {
|
const config = {
|
||||||
entry: {
|
entry: {
|
||||||
@ -13,6 +37,15 @@ const config = {
|
|||||||
library: 'zeroExInstant',
|
library: 'zeroExInstant',
|
||||||
libraryTarget: 'umd',
|
libraryTarget: 'umd',
|
||||||
},
|
},
|
||||||
|
plugins: [
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
'process.env': {
|
||||||
|
GIT_SHA: JSON.stringify(GIT_SHA),
|
||||||
|
HEAP_ANALYTICS_ID: getHeapAnalyticsId(argv.mode),
|
||||||
|
NPM_PACKAGE_VERSION: JSON.stringify(process.env.npm_package_version),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
devtool: 'source-map',
|
devtool: 'source-map',
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.js', '.json', '.ts', '.tsx'],
|
extensions: ['.js', '.json', '.ts', '.tsx'],
|
||||||
@ -36,11 +69,13 @@ const config = {
|
|||||||
after: () => {
|
after: () => {
|
||||||
if (config.devServer.host === '0.0.0.0') {
|
if (config.devServer.host === '0.0.0.0') {
|
||||||
console.log(
|
console.log(
|
||||||
`webpack-dev-server can be accessed externally at: http://${ip.address()}:${config.devServer.port}`,
|
`webpack-dev-server can be accessed externally at: http://${ip.address()}:${
|
||||||
|
config.devServer.port
|
||||||
|
}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
return config;
|
||||||
module.exports = config;
|
};
|
||||||
|
@ -17,6 +17,7 @@ async function prepublishChecksAsync(): Promise<void> {
|
|||||||
await checkChangelogFormatAsync(updatedPublicPackages);
|
await checkChangelogFormatAsync(updatedPublicPackages);
|
||||||
await checkGitTagsForNextVersionAndDeleteIfExistAsync(updatedPublicPackages);
|
await checkGitTagsForNextVersionAndDeleteIfExistAsync(updatedPublicPackages);
|
||||||
await checkPublishRequiredSetupAsync();
|
await checkPublishRequiredSetupAsync();
|
||||||
|
checkRequiredEnvVariables();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkGitTagsForNextVersionAndDeleteIfExistAsync(updatedPublicPackages: Package[]): Promise<void> {
|
async function checkGitTagsForNextVersionAndDeleteIfExistAsync(updatedPublicPackages: Package[]): Promise<void> {
|
||||||
@ -183,6 +184,16 @@ async function checkPublishRequiredSetupAsync(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const checkRequiredEnvVariables = () => {
|
||||||
|
utils.log('Checking required environment variables...');
|
||||||
|
const requiredEnvVars = ['INSTANT_HEAP_ANALYTICS_ID_PRODUCTION'];
|
||||||
|
requiredEnvVars.forEach(requiredEnvVarName => {
|
||||||
|
if (_.isUndefined(process.env[requiredEnvVarName])) {
|
||||||
|
throw new Error(`Must have ${requiredEnvVarName} set`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
prepublishChecksAsync().catch(err => {
|
prepublishChecksAsync().catch(err => {
|
||||||
utils.log(err.message);
|
utils.log(err.message);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user