Skip to content

Commit 68252c1

Browse files
authored
Merge pull request #39 from aha-develop/ADT-449-1-shrink-the-build
ADT-449-1: The new build is rather large
2 parents 4abda3e + b9e9e4e commit 68252c1

10 files changed

Lines changed: 719 additions & 57 deletions

File tree

codegen.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const config: CodegenConfig = {
88
plugins: [
99
"typescript",
1010
"typescript-operations",
11-
"typed-document-node",
11+
"./src/generated/string-typed-node.js",
1212
],
1313
config: {
1414
onlyOperationTypes: true,

package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@aha-develop/github",
33
"description": "GitHub",
4-
"version": "2.0.0",
4+
"version": "2.0.1",
55
"author": "Aha! (support@aha.io)",
66
"repository": {
77
"type": "git",
@@ -13,8 +13,6 @@
1313
"@octokit/graphql-schema": "^13.6.0",
1414
"@octokit/webhooks-types": "^6.3.2",
1515
"gql-tag": "^1.0.1",
16-
"inflected": "^2.1.0",
17-
"moment": "^2.29.3",
1816
"prettier": "^2.6.2"
1917
},
2018
"license": "MIT",

src/components/PrState.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import React from "react";
2-
// @ts-ignore
3-
import { titleize } from "https://cdn.skypack.dev/inflected";
42
import { IPullRequestLink } from "extension";
53

64
const icon = (state: IPullRequestLink["state"]) => {
@@ -17,11 +15,25 @@ const icon = (state: IPullRequestLink["state"]) => {
1715
export const PrState: React.FC<{
1816
state: IPullRequestLink["state"];
1917
}> = ({ state }) => {
18+
let label: string;
19+
20+
switch (state) {
21+
case "closed":
22+
label = "Closed";
23+
break;
24+
case "merged":
25+
label = "Merged";
26+
break;
27+
case "open":
28+
label = "Open";
29+
break;
30+
}
31+
2032
return (
2133
<span className={`pr-state pr-state-${state}`}>
2234
<aha-flex gap="4px">
2335
<aha-icon icon={"fa-regular fa-" + icon(state)}></aha-icon>
24-
<span>{titleize(state)}</span>
36+
<span>{label}</span>
2537
</aha-flex>
2638
</span>
2739
);

src/generated/graphql.ts

Lines changed: 442 additions & 12 deletions
Large diffs are not rendered by default.

src/generated/string-typed-node.js

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// @ts-nocheck
2+
"use strict";
3+
4+
/**
5+
* This file is a copy of document-typed-node but it outputs graphql strings
6+
* instead of compiled graphql document nodes. The reason to do this is that to
7+
* parse the documents again to send with queries requires importing print from
8+
* graphql, which adds a significant amount of code to the output bundle.
9+
*/
10+
11+
const { print } = require("graphql");
12+
const { oldVisit } = require("@graphql-codegen/plugin-helpers");
13+
const {
14+
optimizeOperations,
15+
} = require("@graphql-codegen/visitor-plugin-common");
16+
const { concatAST, Kind } = require("graphql");
17+
const tslib_1 = require("tslib");
18+
const visitor_plugin_common_1 = require("@graphql-codegen/visitor-plugin-common");
19+
const auto_bind_1 = tslib_1.__importDefault(require("auto-bind"));
20+
21+
class TypeScriptDocumentNodesVisitor extends visitor_plugin_common_1.ClientSideBaseVisitor {
22+
constructor(schema, fragments, config, documents) {
23+
super(
24+
schema,
25+
fragments,
26+
{
27+
documentMode:
28+
visitor_plugin_common_1.DocumentMode.documentNodeImportFragments,
29+
documentNodeImport:
30+
"@graphql-typed-document-node/core#TypedDocumentNode",
31+
...config,
32+
},
33+
{},
34+
documents
35+
);
36+
this.pluginConfig = config;
37+
(0, auto_bind_1.default)(this);
38+
// We need to make sure it's there because in this mode, the base plugin doesn't add the import
39+
if (
40+
this.config.documentMode ===
41+
visitor_plugin_common_1.DocumentMode.graphQLTag
42+
) {
43+
const documentNodeImport = this._parseImport(
44+
this.config.documentNodeImport || "graphql#DocumentNode"
45+
);
46+
const tagImport = this._generateImport(
47+
documentNodeImport,
48+
"DocumentNode",
49+
true
50+
);
51+
this._imports.add(tagImport);
52+
}
53+
}
54+
SelectionSet(node, _, parent) {
55+
if (!this.pluginConfig.addTypenameToSelectionSets) {
56+
return;
57+
}
58+
// Don't add __typename to OperationDefinitions.
59+
if (parent && parent.kind === "OperationDefinition") {
60+
return;
61+
}
62+
// No changes if no selections.
63+
const { selections } = node;
64+
if (!selections) {
65+
return;
66+
}
67+
// If selections already have a __typename or is introspection do nothing.
68+
const hasTypename = selections.some(
69+
(selection) =>
70+
selection.kind === "Field" &&
71+
(selection.name.value === "__typename" ||
72+
selection.name.value.lastIndexOf("__", 0) === 0)
73+
);
74+
if (hasTypename) {
75+
return;
76+
}
77+
return {
78+
...node,
79+
selections: [
80+
...selections,
81+
{
82+
kind: "Field",
83+
name: {
84+
kind: "Name",
85+
value: "__typename",
86+
},
87+
},
88+
],
89+
};
90+
}
91+
getDocumentNodeSignature(resultType, variablesTypes, node) {
92+
if (
93+
this.config.documentMode ===
94+
visitor_plugin_common_1.DocumentMode.documentNode ||
95+
this.config.documentMode ===
96+
visitor_plugin_common_1.DocumentMode.documentNodeImportFragments ||
97+
this.config.documentMode ===
98+
visitor_plugin_common_1.DocumentMode.graphQLTag
99+
) {
100+
return ` as unknown as DocumentNode<${resultType}, ${variablesTypes}>`;
101+
}
102+
return super.getDocumentNodeSignature(resultType, variablesTypes, node);
103+
}
104+
_gql(node) {
105+
const document = super._gql(node);
106+
return "`" + print(JSON.parse(document)) + "`";
107+
}
108+
}
109+
110+
module.exports = {
111+
plugin: (schema, rawDocuments, config) => {
112+
const documents = config.flattenGeneratedTypes
113+
? optimizeOperations(schema, rawDocuments)
114+
: rawDocuments;
115+
const allAst = concatAST(documents.map((v) => v.document));
116+
const allFragments = [
117+
...allAst.definitions
118+
.filter((d) => d.kind === Kind.FRAGMENT_DEFINITION)
119+
.map((fragmentDef) => ({
120+
node: fragmentDef,
121+
name: fragmentDef.name.value,
122+
onType: fragmentDef.typeCondition.name.value,
123+
isExternal: false,
124+
})),
125+
...(config.externalFragments || []),
126+
];
127+
const visitor = new TypeScriptDocumentNodesVisitor(
128+
schema,
129+
allFragments,
130+
config,
131+
documents
132+
);
133+
const visitorResult = oldVisit(allAst, { leave: visitor });
134+
return {
135+
prepend: allAst.definitions.length === 0 ? [] : visitor.getImports(),
136+
content: [
137+
visitor.fragments,
138+
...visitorResult.definitions.filter((t) => typeof t === "string"),
139+
].join("\n"),
140+
};
141+
},
142+
};

src/lib/calcTimeElapsed.ts

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,43 @@
1-
import moment from "moment";
2-
31
/**
42
* Calculate Elapsed Time
53
*/
6-
export const calcTimeElapsed = (date: Date | string | number): string => {
7-
if (date instanceof Date) {
8-
return moment(date).fromNow();
4+
export function calcTimeElapsed(inputDate: Date | string) {
5+
const date = inputDate instanceof Date ? inputDate : new Date(inputDate);
6+
7+
const now = new Date();
8+
const diff = now.getTime() - date.getTime();
9+
if (Number.isNaN(diff)) return "";
10+
11+
const seconds = diff / 1000;
12+
const minutes = seconds / 60;
13+
const hours = minutes / 60;
14+
const days = hours / 24;
15+
const weeks = days / 7;
16+
const months = days / 30;
17+
const years = days / 365;
18+
19+
if (seconds < 45) {
20+
return "a few seconds ago";
21+
} else if (years >= 1) {
22+
const roundedYears = Math.round(years);
23+
return `${roundedYears} year${roundedYears !== 1 ? "s" : ""} ago`;
24+
} else if (months >= 1) {
25+
const roundedMonths = Math.round(months);
26+
return `${roundedMonths} month${roundedMonths !== 1 ? "s" : ""} ago`;
27+
} else if (weeks >= 1) {
28+
const roundedWeeks = Math.round(weeks);
29+
return `${roundedWeeks} week${roundedWeeks !== 1 ? "s" : ""} ago`;
30+
} else if (days >= 1) {
31+
const roundedDays = Math.round(days);
32+
return `${roundedDays} day${roundedDays !== 1 ? "s" : ""} ago`;
33+
} else if (hours >= 1) {
34+
const roundedHours = Math.round(hours);
35+
return `${roundedHours} hour${roundedHours !== 1 ? "s" : ""} ago`;
36+
} else if (minutes >= 1) {
37+
const roundedMinutes = Math.round(minutes);
38+
return `${roundedMinutes} minute${roundedMinutes !== 1 ? "s" : ""} ago`;
939
} else {
10-
return moment(new Date(date)).fromNow();
40+
const roundedSeconds = Math.round(seconds);
41+
return `${roundedSeconds} second${roundedSeconds !== 1 ? "s" : ""} ago`;
1142
}
12-
};
43+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { extractReferenceFromName } from "./extractReferenceFromName";
2+
3+
describe("extractReferenceFromName ", () => {
4+
it("should return null if no match", () => {
5+
expect(extractReferenceFromName("foo")).toBe(null);
6+
});
7+
8+
it("returns Requirement", () => {
9+
expect(extractReferenceFromName("FEAT-123-1")).toEqual({
10+
type: "Requirement",
11+
referenceNum: "FEAT-123-1",
12+
});
13+
expect(extractReferenceFromName("in a sentence FEAT-123-1")).toEqual({
14+
type: "Requirement",
15+
referenceNum: "FEAT-123-1",
16+
});
17+
});
18+
19+
it("returns Epic", () => {
20+
expect(extractReferenceFromName("FEAT-E-123")).toEqual({
21+
type: "Epic",
22+
referenceNum: "FEAT-E-123",
23+
});
24+
expect(extractReferenceFromName("in a sentence FEAT-E-123")).toEqual({
25+
type: "Epic",
26+
referenceNum: "FEAT-E-123",
27+
});
28+
});
29+
30+
it("returns Feature", () => {
31+
expect(extractReferenceFromName("FEAT-123")).toEqual({
32+
type: "Feature",
33+
referenceNum: "FEAT-123",
34+
});
35+
expect(extractReferenceFromName("in a sentence FEAT-123")).toEqual({
36+
type: "Feature",
37+
referenceNum: "FEAT-123",
38+
});
39+
});
40+
41+
it("returns the first match", () => {
42+
expect(extractReferenceFromName("with FEAT-123-1 I'm fixing issues that were found in FEAT-122 of epic FEAT-E-121")).toEqual({
43+
type: "Requirement",
44+
referenceNum: "FEAT-123-1",
45+
});
46+
})
47+
});

src/lib/github/api.ts

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,45 @@
1-
import { graphql } from "@octokit/graphql";
2-
import { DocumentNode, print } from "graphql";
31
import { TypedDocumentNode } from "@graphql-typed-document-node/core";
2+
import { DocumentNode } from "graphql";
43

54
/**
65
* Wrap the github provided graphql function in a function that accepts a
76
* graphql document node. This allows us to pass a precompiled document created
87
* from the *.graphql files in ./queries and get typing for the input variables
98
* and output data.
109
*/
11-
function toFetch(api: typeof graphql) {
10+
function toFetch(token: string) {
1211
async function gqlFetch<TData = any, TVariables = Record<string, any>>(
1312
operation: TypedDocumentNode<TData, TVariables>,
1413
variables?: TVariables
1514
): Promise<TData>;
1615
async function gqlFetch<TData = any, TVariables = Record<string, any>>(
17-
operation: DocumentNode,
16+
operation: DocumentNode|string,
17+
variables?: TVariables
18+
): Promise<TData>;
19+
async function gqlFetch<TData = any, TVariables = Record<string, any>>(
20+
operation: any,
1821
variables?: TVariables
1922
): Promise<TData> {
20-
const query = print(operation);
21-
return api(query, variables as any);
23+
const query = operation;
24+
const payload: any = { query };
25+
if (variables) payload.variables = variables;
26+
27+
const response = await fetch("https://api.github.com/graphql", {
28+
method: "POST",
29+
headers: {
30+
"Content-Type": "application/json",
31+
Accept: "application/vnd.github.v3+json",
32+
Authorization: `token ${token}`,
33+
},
34+
body: JSON.stringify(payload),
35+
});
36+
37+
if (!response.ok) {
38+
throw new Error(`${response.status} ${response.statusText}`);
39+
}
40+
41+
const json = await response.json();
42+
return json.data;
2243
}
2344

2445
return gqlFetch;
@@ -30,13 +51,7 @@ export type GqlFetch = ReturnType<typeof toFetch>;
3051
* Take a token and return a wrapped authorized graphql function
3152
*/
3253
export function authedGraphql(token: string): GqlFetch {
33-
return toFetch(
34-
graphql.defaults({
35-
headers: {
36-
authorization: `token ${token}`,
37-
},
38-
})
39-
);
54+
return toFetch(token);
4055
}
4156

4257
/**

0 commit comments

Comments
 (0)