Skip to content

Commit 3e6a496

Browse files
committed
Support direct links
1 parent 4c26f88 commit 3e6a496

File tree

7 files changed

+92
-7
lines changed

7 files changed

+92
-7
lines changed

README.md

-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ This project attempts to analyze npm package contents for issues with their Type
3535
The first things on my roadmap:
3636

3737
- More thorough explanations of problems
38-
- Direct links to results
3938
- Support for DefinitelyTyped analysis
4039
- Official TypeScript module documentation to link to
4140

package-lock.json

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/web/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"license": "MIT",
1717
"dependencies": {
1818
"@arethetypeswrong/core": "file:../core",
19+
"fflate": "^0.7.4",
1920
"immer": "^9.0.21",
2021
"validate-npm-package-name": "^5.0.0"
2122
},

packages/web/src/main.ts

+35-2
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,35 @@
11
import validatePackgeName from "validate-npm-package-name";
22
import type { ResultMessage } from "../worker/worker.ts";
33
import { subscribeRenderer } from "./renderer.ts";
4-
import { updateState, type PackageInfo, type ParsedPackageSpec, getState, subscribe, type State } from "./state.ts";
4+
import {
5+
updateState,
6+
type PackageInfo,
7+
type ParsedPackageSpec,
8+
getState,
9+
subscribe,
10+
type State,
11+
deserializeState,
12+
setState,
13+
serializeState,
14+
} from "./state.ts";
515
import { shallowEqual } from "./utils/shallowEqual.ts";
616

717
// Good grief https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
818
const semverRegex =
919
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
1020
const worker = new Worker(new URL("../worker/worker.ts", import.meta.url), { type: "module" });
11-
worker.onmessage = (event: MessageEvent<ResultMessage>) => {
21+
worker.onmessage = async (event: MessageEvent<ResultMessage>) => {
1222
updateState((state) => {
1323
state.checks = event.data.data;
1424
state.isLoading = false;
1525
state.message = undefined;
1626
});
27+
28+
const state = getState();
29+
const serializedState = await serializeState(state);
30+
const params = new URLSearchParams(location.search);
31+
params.set("s", serializedState);
32+
history.replaceState(null, "", `?${params}`);
1733
};
1834

1935
subscribeRenderer({
@@ -28,6 +44,22 @@ subscribeRenderer({
2844

2945
subscribe(debounce(getPackageInfo, 300));
3046

47+
if (location.search) {
48+
const params = new URLSearchParams(location.search);
49+
const serializedState = params.get("s");
50+
if (serializedState) {
51+
deserializeState(serializedState).then((state) => {
52+
const packageNameInput = document.getElementById("package-spec") as HTMLInputElement;
53+
if (state.packageInfo.parsed) {
54+
packageNameInput.value = `${state.packageInfo.parsed.packageName}${
55+
state.packageInfo.info?.version ? `@${state.packageInfo.info.version}` : ""
56+
}`;
57+
}
58+
setState(state);
59+
});
60+
}
61+
}
62+
3163
async function onPackageNameInput(value: string) {
3264
value = value.trim();
3365
if (!value) {
@@ -113,6 +145,7 @@ async function fetchPackageInfo({ packageName, version }: ParsedPackageSpec): Pr
113145
const data = await response.json();
114146
return {
115147
size: data.dist.unpackedSize,
148+
version: data.version,
116149
};
117150
} catch (error) {
118151
throw new Error("Failed to get package info");

packages/web/src/state.ts

+54
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { Analysis, ProblemSummary, Problem } from "@arethetypeswrong/core";
2+
import { gunzip, gzip } from "fflate";
23
import { produce } from "immer";
34

45
export interface Checks {
@@ -29,6 +30,7 @@ export interface PackageInfoState {
2930

3031
export interface PackageInfo {
3132
size: number | undefined;
33+
version: string;
3234
}
3335

3436
let state: State = {
@@ -58,3 +60,55 @@ export function updateState(updater: (draftState: State) => void): void {
5860
export function getState(): DeepReadonly<State> {
5961
return state;
6062
}
63+
64+
export function setState(newState: State): void {
65+
const prevState = state;
66+
state = newState;
67+
subscribers.forEach((callback) => callback(prevState));
68+
}
69+
70+
export async function serializeState(state = getState()): Promise<string> {
71+
const json = JSON.stringify([state.packageInfo, state.checks], (key, value) => {
72+
if (key === "exports" || key === "trace") return undefined;
73+
return value;
74+
});
75+
76+
return new Promise((resolve, reject) => {
77+
gzip(new TextEncoder().encode(json), (error, data) => {
78+
if (error) {
79+
reject(error);
80+
} else {
81+
resolve(btoa(String.fromCharCode(...data)));
82+
}
83+
});
84+
});
85+
}
86+
87+
export async function deserializeState(serializedState: string): Promise<State> {
88+
const json = new TextDecoder().decode(
89+
await new Promise((resolve, reject) =>
90+
gunzip(
91+
Uint8Array.from(
92+
atob(serializedState)
93+
.split("")
94+
.map((c) => c.charCodeAt(0))
95+
),
96+
(error, data) => {
97+
if (error) {
98+
reject(error);
99+
} else {
100+
resolve(data);
101+
}
102+
}
103+
)
104+
)
105+
);
106+
107+
const [packageInfo, checks] = JSON.parse(json) as [PackageInfoState, Checks];
108+
109+
return {
110+
isLoading: false,
111+
packageInfo,
112+
checks,
113+
};
114+
}

packages/web/worker/patchGlobal.ts

-2
This file was deleted.

packages/web/worker/worker.ts

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import "./patchGlobal";
2-
31
import {
42
checkPackage,
53
checkTgz,

0 commit comments

Comments
 (0)