git-truck 0.8.2 → 0.8.6-experimental
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/bump-version.yml +1 -1
- package/README.md +17 -13
- package/cli.js +2 -0
- package/dev.js +4 -2
- package/package.json +4 -4
- package/server.ts +13 -8
- package/src/analyzer/analyze.server.ts +43 -76
- package/src/analyzer/analyze.test.ts +30 -30
- package/src/analyzer/args.server.ts +20 -6
- package/src/analyzer/constants.ts +1 -1
- package/src/analyzer/git-caller.server.ts +290 -0
- package/src/analyzer/hydrate.server.ts +1 -1
- package/src/analyzer/model.ts +13 -2
- package/src/analyzer/{util.ts → util.server.ts} +27 -33
- package/src/analyzer/util.test.ts +1 -1
- package/src/components/AnalyzingIndicator.tsx +55 -0
- package/src/components/Animations.ts +14 -0
- package/src/components/Chart.tsx +29 -8
- package/src/components/Details.tsx +8 -7
- package/src/components/GlobalInfo.tsx +19 -8
- package/src/components/HiddenFiles.tsx +3 -3
- package/src/components/Legend.tsx +1 -1
- package/src/components/LegendOther.tsx +42 -42
- package/src/components/Main.tsx +1 -1
- package/src/components/SearchBar.tsx +1 -6
- package/src/components/util.tsx +19 -10
- package/src/const.ts +6 -6
- package/src/contexts/ClickedContext.ts +17 -17
- package/src/contexts/DataContext.ts +12 -12
- package/src/contexts/MetricContext.ts +12 -12
- package/src/contexts/OptionsContext.ts +51 -51
- package/src/contexts/SearchContext.ts +19 -19
- package/src/lang-map.d.ts +3 -3
- package/src/metrics.ts +3 -2
- package/src/root.tsx +44 -1
- package/src/routes/{repo.tsx → $repo.tsx} +59 -15
- package/src/routes/index.tsx +156 -46
- package/build/index.js +0 -6836
- package/post-build.js +0 -14
- package/public/favicon.ico +0 -0
- package/src/analyzer/git-caller.ts +0 -117
- package/src/analyzer/index.ts +0 -4
|
@@ -1,30 +1,49 @@
|
|
|
1
|
-
import { ActionFunction, json, LoaderFunction, useLoaderData } from "remix"
|
|
1
|
+
import { ActionFunction, ErrorBoundaryComponent, json, Link, LoaderFunction, useLoaderData } from "remix"
|
|
2
2
|
import { Providers } from "~/components/Providers"
|
|
3
|
-
import { Box, Container, Grower,
|
|
3
|
+
import { Box, Container, Grower, Code, StyledP } from "~/components/util"
|
|
4
4
|
import { SidePanel } from "~/components/SidePanel"
|
|
5
5
|
import { Main } from "~/components/Main"
|
|
6
|
-
import { AnalyzerData } from "~/analyzer/model"
|
|
6
|
+
import { AnalyzerData, TruckUserConfig } from "~/analyzer/model"
|
|
7
7
|
import { analyze, openFile, updateTruckConfig } from "~/analyzer/analyze.server"
|
|
8
8
|
import { GlobalInfo } from "~/components/GlobalInfo"
|
|
9
9
|
import { Options } from "~/components/Options"
|
|
10
10
|
import SearchBar from "~/components/SearchBar"
|
|
11
11
|
import { Spacer } from "~/components/Spacer"
|
|
12
12
|
import { Legend } from "~/components/Legend"
|
|
13
|
-
import {
|
|
13
|
+
import { getTruckConfigWithArgs } from "~/analyzer/args.server"
|
|
14
14
|
import { HiddenFiles } from "~/components/HiddenFiles"
|
|
15
15
|
import semverCompare from "semver-compare"
|
|
16
16
|
import { Details } from "~/components/Details"
|
|
17
|
+
import { resolve } from "path"
|
|
17
18
|
|
|
18
|
-
let
|
|
19
|
+
let invalidateCache = false
|
|
19
20
|
|
|
20
|
-
export const loader: LoaderFunction = async () => {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
export const loader: LoaderFunction = async ({ params, request }) => {
|
|
22
|
+
if (params["repo"] === "favicon.ico") {
|
|
23
|
+
return null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const args = await getTruckConfigWithArgs(params["repo"] as string)
|
|
27
|
+
|
|
28
|
+
const options: TruckUserConfig = {
|
|
29
|
+
invalidateCache: invalidateCache || args.invalidateCache,
|
|
30
|
+
}
|
|
31
|
+
if (params["repo"]) {
|
|
32
|
+
options.path = resolve(args.path, params["repo"])
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const branch = new URL(request.url).searchParams.get("branch")
|
|
36
|
+
if (branch) options.branch = decodeURIComponent(branch)
|
|
37
|
+
|
|
38
|
+
const data = await analyze({ ...args, ...options })
|
|
39
|
+
invalidateCache = false
|
|
24
40
|
return json<AnalyzerData>(data)
|
|
25
41
|
}
|
|
26
42
|
|
|
27
|
-
export const action: ActionFunction = async ({ request }) => {
|
|
43
|
+
export const action: ActionFunction = async ({ request, params }) => {
|
|
44
|
+
if (!params["repo"]) {
|
|
45
|
+
throw Error("This can never happen, since this route is only called if a repo exists in the URL")
|
|
46
|
+
}
|
|
28
47
|
const formData = await request.formData()
|
|
29
48
|
const refresh = formData.get("refresh")
|
|
30
49
|
const unignore = formData.get("unignore")
|
|
@@ -32,12 +51,15 @@ export const action: ActionFunction = async ({ request }) => {
|
|
|
32
51
|
const fileToOpen = formData.get("open")
|
|
33
52
|
|
|
34
53
|
if (refresh) {
|
|
35
|
-
|
|
54
|
+
invalidateCache = true
|
|
36
55
|
return null
|
|
37
56
|
}
|
|
38
57
|
|
|
58
|
+
const args = await getTruckConfigWithArgs(params["repo"])
|
|
59
|
+
const path = resolve(args.path, params["repo"])
|
|
60
|
+
|
|
39
61
|
if (ignore && typeof ignore === "string") {
|
|
40
|
-
await updateTruckConfig(
|
|
62
|
+
await updateTruckConfig(path, (prevConfig) => {
|
|
41
63
|
const hiddenFilesSet = new Set((prevConfig?.hiddenFiles ?? []).map((x) => x.trim()))
|
|
42
64
|
hiddenFilesSet.add(ignore)
|
|
43
65
|
|
|
@@ -50,7 +72,7 @@ export const action: ActionFunction = async ({ request }) => {
|
|
|
50
72
|
}
|
|
51
73
|
|
|
52
74
|
if (unignore && typeof unignore === "string") {
|
|
53
|
-
await updateTruckConfig((
|
|
75
|
+
await updateTruckConfig(resolve(args.path, params["repo"]), (prevConfig) => {
|
|
54
76
|
const hiddenFilesSet = new Set((prevConfig?.hiddenFiles ?? []).map((x) => x.trim()))
|
|
55
77
|
hiddenFilesSet.delete(unignore.trim())
|
|
56
78
|
|
|
@@ -70,9 +92,32 @@ export const action: ActionFunction = async ({ request }) => {
|
|
|
70
92
|
return null
|
|
71
93
|
}
|
|
72
94
|
|
|
95
|
+
export const ErrorBoundary: ErrorBoundaryComponent = ({ error }) => {
|
|
96
|
+
console.error(error.message)
|
|
97
|
+
console.error(error.stack)
|
|
98
|
+
return (
|
|
99
|
+
<Container>
|
|
100
|
+
<div />
|
|
101
|
+
<Box>
|
|
102
|
+
<h1>An error occured!</h1>
|
|
103
|
+
<p>See console for more infomation.</p>
|
|
104
|
+
<Code>{error.stack}</Code>
|
|
105
|
+
<div>
|
|
106
|
+
<Link to=".">Retry</Link>
|
|
107
|
+
</div>
|
|
108
|
+
<div>
|
|
109
|
+
<Link to="..">Go back</Link>
|
|
110
|
+
</div>
|
|
111
|
+
</Box>
|
|
112
|
+
</Container>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
73
116
|
export default function Index() {
|
|
74
117
|
const data = useLoaderData<AnalyzerData>()
|
|
75
118
|
|
|
119
|
+
if (!data) return null
|
|
120
|
+
|
|
76
121
|
return (
|
|
77
122
|
<Providers data={data}>
|
|
78
123
|
<Container>
|
|
@@ -89,8 +134,7 @@ export default function Index() {
|
|
|
89
134
|
<p>Update available: {data.latestVersion}</p>
|
|
90
135
|
<StyledP>Currently installed: {data.currentVersion}</StyledP>
|
|
91
136
|
<StyledP>
|
|
92
|
-
To update, close application and run:
|
|
93
|
-
<InlineCode>npx git-truck@latest</InlineCode>
|
|
137
|
+
To update, close application and run: <Code inline>npx git-truck@latest</Code>
|
|
94
138
|
</StyledP>
|
|
95
139
|
</Box>
|
|
96
140
|
) : null}
|
package/src/routes/index.tsx
CHANGED
|
@@ -1,62 +1,172 @@
|
|
|
1
|
-
import { useNavigate } from "
|
|
2
|
-
import { useEffect } from "react"
|
|
1
|
+
import { ActionFunction, json, Link, LoaderFunction, useLoaderData, useNavigate, useSubmit, useTransition } from "remix"
|
|
3
2
|
import styled from "styled-components"
|
|
3
|
+
import { getArgsWithDefaults } from "~/analyzer/args.server"
|
|
4
|
+
import { getBaseDirFromPath, getDirName } from "~/analyzer/util.server"
|
|
5
|
+
import { Spacer } from "~/components/Spacer"
|
|
6
|
+
import { Box, BoxSubTitle, Code, Grower, TextButton } from "~/components/util"
|
|
7
|
+
import { AnalyzingIndicator } from "~/components/AnalyzingIndicator"
|
|
8
|
+
import { resolve } from "path"
|
|
9
|
+
import { Repository } from "~/analyzer/model"
|
|
10
|
+
import { GitCaller } from "~/analyzer/git-caller.server"
|
|
11
|
+
import { useMount } from "react-use"
|
|
4
12
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
13
|
+
interface IndexData {
|
|
14
|
+
repositories: Repository[]
|
|
15
|
+
baseDir: string
|
|
16
|
+
baseDirName: string
|
|
17
|
+
repo: Repository | null
|
|
18
|
+
hasRedirected: boolean
|
|
19
|
+
}
|
|
10
20
|
|
|
11
|
-
|
|
12
|
-
opacity: 0;
|
|
13
|
-
animation: hide_initially 0s linear forwards;
|
|
14
|
-
animation-delay: 1s;
|
|
15
|
-
`
|
|
21
|
+
let hasRedirected = false
|
|
16
22
|
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
export const loader: LoaderFunction = async () => {
|
|
24
|
+
const args = await getArgsWithDefaults()
|
|
25
|
+
const [repo, repositories] = await GitCaller.scanDirectoryForRepositories(args.path)
|
|
26
|
+
const baseDir = resolve(repo ? getBaseDirFromPath(args.path) : args.path)
|
|
27
|
+
const repositoriesResponse = json<IndexData>({
|
|
28
|
+
repositories,
|
|
29
|
+
baseDir,
|
|
30
|
+
baseDirName: getDirName(baseDir),
|
|
31
|
+
repo,
|
|
32
|
+
hasRedirected,
|
|
33
|
+
})
|
|
23
34
|
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
stroke: #4580ff;
|
|
27
|
-
/* transition: 1s; */
|
|
28
|
-
animation: dash 2s ease-in-out alternate infinite;
|
|
29
|
-
`
|
|
35
|
+
const response = repositoriesResponse
|
|
36
|
+
hasRedirected = true
|
|
30
37
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
`
|
|
38
|
+
return response
|
|
39
|
+
}
|
|
34
40
|
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
export const action: ActionFunction = async ({ request }) => {
|
|
42
|
+
const formData = await request.formData()
|
|
43
|
+
if (formData.has("hasRedirected")) {
|
|
44
|
+
hasRedirected = true
|
|
45
|
+
}
|
|
46
|
+
return null
|
|
47
|
+
}
|
|
38
48
|
|
|
39
49
|
export default function Index() {
|
|
50
|
+
const loadterData = useLoaderData<IndexData>()
|
|
51
|
+
const { repositories, baseDir, baseDirName, repo, hasRedirected } = loadterData
|
|
52
|
+
const transitionData = useTransition()
|
|
40
53
|
const navigate = useNavigate()
|
|
41
|
-
|
|
42
|
-
navigate("/repo/")
|
|
43
|
-
}, [navigate])
|
|
54
|
+
const submit = useSubmit()
|
|
44
55
|
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
56
|
+
const willRedirect = repo && !hasRedirected
|
|
57
|
+
useMount(() => {
|
|
58
|
+
if (willRedirect) {
|
|
59
|
+
const data = new FormData()
|
|
60
|
+
data.append("hasRedirected", "true")
|
|
61
|
+
submit(data, { method: "post" })
|
|
62
|
+
navigate(`/${repo.name}`)
|
|
63
|
+
}
|
|
64
|
+
})
|
|
48
65
|
|
|
49
|
-
const
|
|
50
|
-
const
|
|
66
|
+
const cachedRepositories = repositories.filter((repo) => repo.data?.cached)
|
|
67
|
+
const notCachedRepositories = repositories.filter((repo) => !repo.data?.cached)
|
|
51
68
|
|
|
69
|
+
if (transitionData.state !== "idle" || willRedirect) return <AnalyzingIndicator />
|
|
52
70
|
return (
|
|
53
|
-
<
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
</
|
|
59
|
-
</
|
|
60
|
-
|
|
71
|
+
<Wrapper>
|
|
72
|
+
<Spacer />
|
|
73
|
+
<H1>{baseDir}</H1>
|
|
74
|
+
<Spacer />
|
|
75
|
+
<p>
|
|
76
|
+
Found {repositories.length} git repositories in the folder <Code inline>{baseDirName}</Code>.
|
|
77
|
+
</p>
|
|
78
|
+
{repositories.length === 0 ? (
|
|
79
|
+
<>
|
|
80
|
+
<Spacer />
|
|
81
|
+
<p>
|
|
82
|
+
Try running <Code inline>git-truck</Code> in another folder or provide another path as argument.
|
|
83
|
+
</p>
|
|
84
|
+
</>
|
|
85
|
+
) : null}
|
|
86
|
+
{cachedRepositories.length > 0 ? (
|
|
87
|
+
<>
|
|
88
|
+
<Spacer xxl />
|
|
89
|
+
<h2>Ready to view</h2>
|
|
90
|
+
<nav>
|
|
91
|
+
<Ul>
|
|
92
|
+
{cachedRepositories.map((repo) => (
|
|
93
|
+
<Li key={repo.name}>
|
|
94
|
+
<SLink to={repo.name} tabIndex={-1}>
|
|
95
|
+
<Box>
|
|
96
|
+
<BoxSubTitle title={repo.name}>{repo.name}</BoxSubTitle>
|
|
97
|
+
<Spacer />
|
|
98
|
+
<Actions>
|
|
99
|
+
<Grower />
|
|
100
|
+
<TextButton>{repo.data?.cached ? "View" : "Analyze"}</TextButton>
|
|
101
|
+
</Actions>
|
|
102
|
+
</Box>
|
|
103
|
+
</SLink>
|
|
104
|
+
</Li>
|
|
105
|
+
))}
|
|
106
|
+
</Ul>
|
|
107
|
+
</nav>
|
|
108
|
+
</>
|
|
109
|
+
) : null}
|
|
110
|
+
{notCachedRepositories.length > 0 ? (
|
|
111
|
+
<>
|
|
112
|
+
<Spacer xxl />
|
|
113
|
+
<h2>Needs to be analyzed before viewing</h2>
|
|
114
|
+
<nav>
|
|
115
|
+
<Ul>
|
|
116
|
+
{notCachedRepositories.map((repo) => (
|
|
117
|
+
<Li key={repo.name}>
|
|
118
|
+
<SLink to={repo.name} tabIndex={-1}>
|
|
119
|
+
<Box>
|
|
120
|
+
<BoxSubTitle title={repo.name}>{repo.name}</BoxSubTitle>
|
|
121
|
+
<Spacer />
|
|
122
|
+
<Actions>
|
|
123
|
+
<Grower />
|
|
124
|
+
<TextButton>{repo.data?.cached ? "View" : "Analyze"}</TextButton>
|
|
125
|
+
</Actions>
|
|
126
|
+
</Box>
|
|
127
|
+
</SLink>
|
|
128
|
+
</Li>
|
|
129
|
+
))}
|
|
130
|
+
</Ul>
|
|
131
|
+
</nav>
|
|
132
|
+
</>
|
|
133
|
+
) : null}
|
|
134
|
+
</Wrapper>
|
|
61
135
|
)
|
|
62
136
|
}
|
|
137
|
+
|
|
138
|
+
const Wrapper = styled.div`
|
|
139
|
+
width: calc(100vw - 2 * var(--side-panel-width));
|
|
140
|
+
margin: auto;
|
|
141
|
+
`
|
|
142
|
+
const H1 = styled.h1`
|
|
143
|
+
font-family: "Courier New", Courier, monospace;
|
|
144
|
+
`
|
|
145
|
+
|
|
146
|
+
const Ul = styled.ul`
|
|
147
|
+
list-style: none;
|
|
148
|
+
display: grid;
|
|
149
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
150
|
+
`
|
|
151
|
+
|
|
152
|
+
const Li = styled.li`
|
|
153
|
+
margin: 0;
|
|
154
|
+
`
|
|
155
|
+
|
|
156
|
+
const Tag = styled.span`
|
|
157
|
+
font-size: 0.7em;
|
|
158
|
+
text-transform: uppercase;
|
|
159
|
+
padding: calc(0.25 * var(--unit)) calc(var(--unit));
|
|
160
|
+
letter-spacing: 0.2em;
|
|
161
|
+
color: white;
|
|
162
|
+
background-color: hsl(210, 100%, 50%);
|
|
163
|
+
border-radius: 1000px;
|
|
164
|
+
`
|
|
165
|
+
|
|
166
|
+
const Actions = styled.div`
|
|
167
|
+
display: flex;
|
|
168
|
+
`
|
|
169
|
+
|
|
170
|
+
const SLink = styled(Link)`
|
|
171
|
+
text-decoration: none;
|
|
172
|
+
`
|