Skip to content

Commit

Permalink
added ts strict mode and fixed resulting issues
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Mrowetz committed Mar 16, 2018
1 parent b43e4de commit e2d0afc
Show file tree
Hide file tree
Showing 23 changed files with 171 additions and 132 deletions.
4 changes: 2 additions & 2 deletions src/ts/file-reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ zip.useWebWorkers = false;
/** handle client side file upload */
export function readFile(file: File,
fileName: string,
callback: (e: Error, har?: Har) => void,
callback: (e: Error | null, har?: Har) => void,
onProgress?: (progress: number) => void) {
if (!file) {
return callback(new Error("Failed to load HAR file"));
Expand All @@ -24,7 +24,7 @@ export function readFile(file: File,
}

/** start reading the file */
const extension = fileName.match(/\.[0-9a-z]+$/i)[0];
const extension = (fileName.match(/\.[0-9a-z]+$/i) || [])[0];
if ([".zhar", ".zip"].indexOf(extension) !== -1) {
/** zhar */
zip.createReader(new zip.BlobReader(file), (zipReader) => {
Expand Down
12 changes: 6 additions & 6 deletions src/ts/helpers/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function removeClass<T extends Element>(el: T, className: string): T {
classList.remove(className);
} else {
// IE doesn't support classList in SVG
el.setAttribute("class", el.getAttribute("class")
el.setAttribute("class", (el.getAttribute("class") || "")
.replace(new RegExp("(\\s|^)" + className + "(\\s|$)", "g"), "$2"));
}
return el;
Expand All @@ -44,9 +44,9 @@ export function getParentByClassName(base: Element, className: string) {
if (base.classList.contains(className)) {
return base;
}
base = base.parentElement;
base = base.parentElement as Element;
}
return undefined;
return null;
}

/**
Expand All @@ -55,7 +55,7 @@ export function getParentByClassName(base: Element, className: string) {
*/
export function removeChildren<T extends Element>(el: T): T {
while (el.hasChildNodes()) {
el.removeChild(el.lastChild);
el.removeChild(el.lastChild as Element);
}
return el;
}
Expand All @@ -64,7 +64,7 @@ export function removeChildren<T extends Element>(el: T): T {
* Get last element of `NodeList`
* @param list NodeListOf e.g. return value of `getElementsByClassName`
*/
export function getLastItemOfNodeList<T extends Node>(list: NodeListOf<T>) {
export function getLastItemOfNodeList<T extends Node>(list: NodeListOf<T> | null) {
if (!list || list.length === 0) {
return undefined;
}
Expand Down Expand Up @@ -96,7 +96,7 @@ export function safeSetAttribute(el: HTMLElement | SVGElement, name: string, val
console.warn(new Error(`Trying to set non-existing attribute ` +
`${name} = ${value} on a <${el.tagName.toLowerCase()}>.`));
}
el.setAttributeNS(null, name, value);
el.setAttributeNS("", name, value);
}

/** Sets multiple CSS style properties, but only if property exists on `el` */
Expand Down
2 changes: 1 addition & 1 deletion src/ts/helpers/misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*/
function parseUrl(url: string) {
const pattern = RegExp("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?");
const matches = url.match(pattern);
const matches = url.match(pattern) || [];
return {
authority: matches[4],
fragment: matches[9],
Expand Down
56 changes: 36 additions & 20 deletions src/ts/helpers/parse.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { ChartRenderOption } from "../typing/options";
import { roundNumber } from "./misc";

export type MaybeStringOrNumber = string | number | undefined | null;

/**
* Type safe and null safe way to transform, filter and format an input value, e.g. parse a Date from a string,
* rejecting invalid dates, and formatting it as a localized string. If the input value is undefined, or the parseFn
Expand All @@ -10,9 +12,9 @@ import { roundNumber } from "./misc";
* @param formatFn an optional function to format the parsed input value.
* @returns {string} a formatted string representation of the input, or undefined.
*/
export function parseAndFormat<S, T>(input: S,
export function parseAndFormat<S, T>(input: S | undefined,
parseFn: ((_: S) => T),
formatFn: ((_: T) => string) = toString): string {
formatFn: ((_: T) => string | undefined) = toString): string | undefined {
if (input === undefined) {
return undefined;
}
Expand All @@ -27,33 +29,39 @@ function toString<T>(source: T): string {
if (typeof source["toString"] === "function") {
return source.toString();
} else {
throw TypeError("Can't convert type ${typeof source} to string");
throw TypeError(`Can't convert type ${typeof source} to string`);
}
}

export function parseNonEmpty(input: string): string {
export function parseNonEmpty(input: string): string | undefined {
return input.trim().length > 0 ? input : undefined;
}

export function parseDate(input: string): Date {
export function parseDate(input: string): Date | undefined {
const date = new Date(input);
if (isNaN(date.getTime())) {
return undefined;
}
return date;
}

export function parseNonNegative(input: string | number): number {
export function parseNonNegative(input: MaybeStringOrNumber): number | undefined {
if (input === undefined || input === null) {
return undefined;
}
const filter = (n) => (n >= 0);
return parseToNumber(input, filter);
}

export function parsePositive(input: string | number): number {
export function parsePositive(input: MaybeStringOrNumber): number | undefined {
if (input === undefined || input === null) {
return undefined;
}
const filter = (n) => (n > 0);
return parseToNumber(input, filter);
}

function parseToNumber(input: string | number, filterFn: (_: number) => boolean): number {
function parseToNumber(input: string | number, filterFn: (_: number) => boolean): number | undefined {
const filter = (n: number) => filterFn(n) ? n : undefined;

if (typeof input === "string") {
Expand All @@ -66,15 +74,19 @@ function parseToNumber(input: string | number, filterFn: (_: number) => boolean)
return filter(input);
}

export function formatMilliseconds(millis: number): string {
return `${roundNumber(millis, 3)} ms`;
export function formatMilliseconds(millis: number | undefined): string | undefined {
return (millis !== undefined) ? `${roundNumber(millis, 3)} ms` : undefined;
}

const secondsPerMinute = 60;
const secondsPerHour = 60 * secondsPerMinute;
const secondsPerDay = 24 * secondsPerHour;

export function formatSeconds(seconds: number): string {
export function formatSeconds(seconds: number | undefined): string | undefined {
if (seconds === undefined) {
return undefined;
}

const raw = `${roundNumber(seconds, 3)} s`;
if (seconds > secondsPerDay) {
return `${raw} (~${roundNumber(seconds / secondsPerDay, 0)} days)`;
Expand All @@ -88,14 +100,17 @@ export function formatSeconds(seconds: number): string {
return raw;
}

export function formatDateLocalized(date: Date): string {
return `${date.toUTCString()}<br/>(local time: ${date.toLocaleString()})`;
export function formatDateLocalized(date: Date | undefined): string | undefined {
return (date !== undefined) ? `${date.toUTCString()}<br/>(local time: ${date.toLocaleString()})` : undefined;
}

const bytesPerKB = 1024;
const bytesPerMB = 1024 * bytesPerKB;

export function formatBytes(bytes: number): string {
export function formatBytes(bytes: number | undefined): string {
if (bytes === undefined) {
return "";
}
const raw = `${bytes} bytes`;
if (bytes >= bytesPerMB) {
return `${raw} (~${roundNumber(bytes / bytesPerMB, 1)} MB)`;
Expand Down Expand Up @@ -124,8 +139,8 @@ const htmlChars = new RegExp(Object.keys(htmlCharMap).join("|"), "g");
* Escapes unsafe characters in a string to render safely in HTML
* @param {string} unsafe - string to be rendered in HTML
*/
export function escapeHtml(unsafe: string | number | boolean = ""): string {
if (unsafe === null) {
export function escapeHtml(unsafe: MaybeStringOrNumber | boolean = ""): string {
if (unsafe === null || unsafe === undefined) {
return ""; // See https://github.com/micmro/PerfCascade/issues/217
}
if (typeof unsafe !== "string") {
Expand All @@ -149,7 +164,7 @@ export function sanitizeUrlForLink(unsafeUrl: string) {
if (cleaned.indexOf("http://") === 0 || cleaned.indexOf("https://") === 0) {
return cleaned;
}
// tslint:disable-next-line:no-console
// tslint:disable-next-line:no-console
console.warn("skipped link, due to potentially unsafe url", unsafeUrl);
return "";
}
Expand All @@ -163,7 +178,7 @@ export function sanitizeAlphaNumeric(unsafe: string | number) {
}

/** Ensures `input` is casted to `number` */
export function toInt(input: string | number): number {
export function toInt(input: MaybeStringOrNumber): number | undefined {
if (typeof input === "number") {
return input;
} else if (typeof input === "string") {
Expand All @@ -176,10 +191,11 @@ export function toInt(input: string | number): number {
/** Validates the `ChartOptions` attributes types */
export function validateOptions(options: ChartRenderOption): ChartRenderOption {
const validateInt = (name: keyof ChartRenderOption) => {
options[name] = toInt(options[name] as any);
if (options[name] === undefined) {
const val = toInt(options[name] as any);
if (val === undefined) {
throw TypeError(`option "${name}" needs to be a number`);
}
options[name] = val;
};
const ensureBoolean = (name: keyof ChartRenderOption) => {
options[name] = !!options[name];
Expand Down
4 changes: 2 additions & 2 deletions src/ts/helpers/svg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ const getTestSVGEl = (() => {
// debounced time-deleayed cleanup, so the element can be re-used in tight loops
clearTimeout(removeSvgTestElTimeout);
removeSvgTestElTimeout = setTimeout(() => {
svgTestEl.parentNode.removeChild(svgTestEl);
(svgTestEl.parentNode as Node).removeChild(svgTestEl);
}, 500);

return svgTestEl;
Expand All @@ -136,7 +136,7 @@ const getTestSVGEl = (() => {
* @returns number
*/
export function getNodeTextWidth(textNode: SVGTextElement, skipClone: boolean = false): number {
if (textNode.textContent.length === 0) {
if ((textNode.textContent || "").length === 0) {
return 0;
}
const tmp = getTestSVGEl();
Expand Down
2 changes: 1 addition & 1 deletion src/ts/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ function PerfCascade(waterfallDocsData: WaterfallDocs, chartOptions: Partial<Cha

// page update behaviour
paging.onPageUpdate((_pageIndex, pageDoc) => {
const el = doc.parentElement;
const el = doc.parentElement as HTMLElement;
const newDoc = createWaterfallSvg(pageDoc, options);
el.replaceChild(newDoc, doc);
doc = newDoc;
Expand Down
2 changes: 1 addition & 1 deletion src/ts/paging/paging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export default class Paging {
* @param {OnPagingCb} cb
* @returns number - index of the callback
*/
public onPageUpdate(cb: OnPagingCb): number {
public onPageUpdate(cb: OnPagingCb): number | undefined {
if (this.getPageCount() > 1) {
return this.onPageUpdateCbs.push(cb);
}
Expand Down
31 changes: 16 additions & 15 deletions src/ts/transformers/extract-details-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,20 @@ import {
formatDateLocalized,
formatMilliseconds,
formatSeconds,
MaybeStringOrNumber,
parseAndFormat,
parseDate,
parseNonEmpty,
parseNonNegative,
parsePositive,
} from "../helpers/parse";
import { KvTuple } from "../typing/waterfall";
import { KvTuple, SafeKvTuple } from "../typing/waterfall";
import { flattenKvTuple } from "./helpers";

const byteSizeProperty = (title: string, input: string |  number): KvTuple => {
const byteSizeProperty = (title: string, input: MaybeStringOrNumber): KvTuple => {
return [title, parseAndFormat(input, parsePositive, formatBytes)];
};
const countProperty = (title: string, input: string |  number): KvTuple => {
const countProperty = (title: string, input: MaybeStringOrNumber): KvTuple => {
return [title, parseAndFormat(input, parsePositive)];
};

Expand All @@ -26,7 +27,7 @@ const notEmpty = (kv: KvTuple) => {
return kv.length > 1 && kv[1] !== undefined && kv[1] !== "";
};

function parseGeneralDetails(entry: Entry, startRelative: number, requestID: number): KvTuple[] {
function parseGeneralDetails(entry: Entry, startRelative: number, requestID: number): SafeKvTuple[] {
return ([
["Request Number", `#${requestID}`],
["Started", new Date(entry.startedDateTime).toLocaleString() + ((startRelative > 0) ?
Expand Down Expand Up @@ -55,10 +56,10 @@ function parseGeneralDetails(entry: Entry, startRelative: number, requestID: num
byteSizeProperty("Minify Save", entry._minify_save),
byteSizeProperty("Image Total", entry._image_total),
byteSizeProperty("Image Save", entry._image_save),
] as KvTuple[]).filter(notEmpty);
] as KvTuple[]).filter(notEmpty) as SafeKvTuple[];
}

function parseRequestDetails(harEntry: Entry): KvTuple[] {
function parseRequestDetails(harEntry: Entry): SafeKvTuple[] {
const request = harEntry.request;
const stringHeader = (name: string): KvTuple[] => getHeaders(request.headers, name);

Expand All @@ -81,10 +82,10 @@ function parseRequestDetails(harEntry: Entry): KvTuple[] {
stringHeader("If-Unmodified-Since"),
countProperty("Querystring parameters count", request.queryString.length),
countProperty("Cookies count", request.cookies.length),
]).filter(notEmpty);
]).filter(notEmpty) as SafeKvTuple[];
}

function parseResponseDetails(entry: Entry): KvTuple[] {
function parseResponseDetails(entry: Entry): SafeKvTuple[] {
const response = entry.response;
const content = response.content;
const headers = response.headers;
Expand Down Expand Up @@ -138,20 +139,20 @@ function parseResponseDetails(entry: Entry): KvTuple[] {
stringHeader("Timing-Allow-Origin"),
["Redirect URL", parseAndFormat(response.redirectURL, parseNonEmpty)],
["Comment", parseAndFormat(response.comment, parseNonEmpty)],
]).filter(notEmpty);
]).filter(notEmpty) as SafeKvTuple[];
}

function parseTimings(entry: Entry, start: number, end: number): KvTuple[] {
function parseTimings(entry: Entry, start: number, end: number): SafeKvTuple[] {
const timings = entry.timings;
const optionalTiming = (timing?: number) => parseAndFormat(timing, parseNonNegative, formatMilliseconds);
const total = (typeof start !== "number" || typeof end !== "number") ? undefined : (end - start);

let connectVal = optionalTiming(timings.connect);
if (timings.ssl > 0) {
if (timings.ssl && timings.ssl > 0 && timings.connect) {
// SSL time is also included in the connect field (to ensure backward compatibility with HAR 1.1).
connectVal = `${connectVal} (without TLS: ${optionalTiming(timings.connect - timings.ssl)})`;
}
return [
return ([
["Total", formatMilliseconds(total)],
["Blocked", optionalTiming(timings.blocked)],
["DNS", optionalTiming(timings.dns)],
Expand All @@ -160,7 +161,7 @@ function parseTimings(entry: Entry, start: number, end: number): KvTuple[] {
["Send", formatMilliseconds(timings.send)],
["Wait", formatMilliseconds(timings.wait)],
["Receive", formatMilliseconds(timings.receive)],
];
] as KvTuple[]).filter(notEmpty) as SafeKvTuple[];
}

/**
Expand All @@ -176,9 +177,9 @@ export function getKeys(entry: Entry, requestID: number, startRelative: number,
return {
general: parseGeneralDetails(entry, startRelative, requestID),
request: parseRequestDetails(entry),
requestHeaders: requestHeaders.map(headerToKvTuple),
requestHeaders: requestHeaders.map(headerToKvTuple).filter(notEmpty) as SafeKvTuple[],
response: parseResponseDetails(entry),
responseHeaders: responseHeaders.map(headerToKvTuple),
responseHeaders: responseHeaders.map(headerToKvTuple).filter(notEmpty) as SafeKvTuple[],
timings: parseTimings(entry, startRelative, endRelative),
};
}
3 changes: 3 additions & 0 deletions src/ts/transformers/har-heuristics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ function isSecure(entry: Entry) {
}

function isPush(entry: Entry): boolean {
if (entry._was_pushed === undefined || entry._was_pushed === null) {
return false;
}
function toInt(input: string | number): number {
if (typeof input === "string") {
return parseInt(input, 10);
Expand Down
Loading

0 comments on commit e2d0afc

Please sign in to comment.