Compare commits
14 Commits
usage-stat
...
v2.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4489504b7a | ||
|
|
027f89563f | ||
|
|
7233ec9e15 | ||
|
|
54dbbbf73d | ||
|
|
3eed9ce6d6 | ||
|
|
3acebc09eb | ||
|
|
82417bf9f5 | ||
|
|
7b6d52d221 | ||
|
|
fd133c88c7 | ||
|
|
229c9a9cd6 | ||
|
|
3803e1ccfa | ||
|
|
8ca289e908 | ||
|
|
fd7cc1f05a | ||
|
|
632fcf6cb7 |
22
.github/workflows/main.yml
vendored
22
.github/workflows/main.yml
vendored
@@ -7,21 +7,21 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: yarn
|
||||
node-version: 16
|
||||
- run: yarn install --frozen-lockfile
|
||||
|
||||
- run: yarn type
|
||||
- run: yarn lint
|
||||
- run: yarn test --ci
|
||||
- run: npm run type
|
||||
- run: npm run lint
|
||||
- run: npm run test --ci
|
||||
|
||||
test-action:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: update action.yml to use image from local Dockerfile
|
||||
run: |
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
|
||||
- name: build svg-only action
|
||||
run: |
|
||||
yarn build:action
|
||||
npm run build:action
|
||||
rm -r svg-only/dist
|
||||
mv packages/action/dist svg-only/dist
|
||||
|
||||
@@ -92,18 +92,18 @@ jobs:
|
||||
deploy-ghpages:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: yarn
|
||||
node-version: 16
|
||||
- run: yarn install --frozen-lockfile
|
||||
|
||||
- run: yarn build:demo
|
||||
- run: npm run build:demo
|
||||
env:
|
||||
GITHUB_USER_CONTRIBUTION_API_ENDPOINT: https://snk-one.vercel.app/api/github-user-contribution/
|
||||
|
||||
- uses: crazy-max/ghaction-github-pages@v2.6.0
|
||||
- uses: crazy-max/ghaction-github-pages@v3.1.0
|
||||
if: success() && github.ref == 'refs/heads/main'
|
||||
with:
|
||||
target_branch: gh-pages
|
||||
|
||||
16
.github/workflows/release.yml
vendored
16
.github/workflows/release.yml
vendored
@@ -21,19 +21,19 @@ jobs:
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: docker/setup-qemu-action@v1
|
||||
- uses: docker/setup-qemu-action@v2
|
||||
|
||||
- uses: docker/setup-buildx-action@v1
|
||||
- uses: docker/setup-buildx-action@v2
|
||||
|
||||
- uses: docker/login-action@v1
|
||||
- uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: build and publish the docker image
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v4
|
||||
id: docker-build
|
||||
with:
|
||||
push: true
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
run: |
|
||||
sed -i "s/image: .*/image: docker:\/\/platane\/snk@${{ steps.docker-build.outputs.digest }}/" action.yml
|
||||
|
||||
- uses: actions/setup-node@v2
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: yarn
|
||||
node-version: 16
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
- name: build svg-only action
|
||||
run: |
|
||||
yarn install --frozen-lockfile
|
||||
yarn build:action
|
||||
npm run build:action
|
||||
rm -r svg-only/dist
|
||||
mv packages/action/dist svg-only/dist
|
||||
|
||||
@@ -81,7 +81,7 @@ jobs:
|
||||
echo "prerelease=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- uses: ncipollo/release-action@v1.11.1
|
||||
- uses: ncipollo/release-action@v1.12.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
|
||||
26
README.md
26
README.md
@@ -8,7 +8,20 @@
|
||||
|
||||
Generates a snake game from a github user contributions graph
|
||||
|
||||

|
||||
<picture>
|
||||
<source
|
||||
media="(prefers-color-scheme: dark)"
|
||||
srcset="https://raw.githubusercontent.com/platane/snk/output/github-contribution-grid-snake-dark.svg"
|
||||
/>
|
||||
<source
|
||||
media="(prefers-color-scheme: light)"
|
||||
srcset="https://raw.githubusercontent.com/platane/snk/output/github-contribution-grid-snake.svg"
|
||||
/>
|
||||
<img
|
||||
alt="github contribution grid snake animation"
|
||||
src="https://raw.githubusercontent.com/platane/snk/output/github-contribution-grid-snake.svg"
|
||||
/>
|
||||
</picture>
|
||||
|
||||
Pull a github user's contribution graph.
|
||||
Make it a snake Game, generate a snake path where the cells get eaten in an orderly fashion.
|
||||
@@ -49,11 +62,14 @@ If you are only interested in generating a svg, consider using this faster actio
|
||||
|
||||
**dark mode**
|
||||
|
||||
For **dark mode** support on github, use this [special syntax](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#specifying-the-theme-an-image-is-shown-to=) in your readme.
|
||||
For **dark mode** support on github, use this [special syntax](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#specifying-the-theme-an-image-is-shown-to) in your readme.
|
||||
|
||||
```md
|
||||

|
||||

|
||||
```html
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="github-snake-dark.svg" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="github-snake.svg" />
|
||||
<img alt="github-snake" src="github-snake.svg" />
|
||||
</picture>
|
||||
```
|
||||
|
||||
**interactive demo**
|
||||
|
||||
@@ -4,7 +4,7 @@ author: "platane"
|
||||
|
||||
runs:
|
||||
using: docker
|
||||
image: docker://platane/snk@sha256:dcb351bdad223f2a2161fa5d6e3c9102e6ebe9fbde99a10fa3bf443d69f61a0f
|
||||
image: docker://platane/snk@sha256:2115ffeb538e355aa155630e6e32b6d77ea2345fa8584645c41ace7f5ad667fc
|
||||
|
||||
inputs:
|
||||
github_user_name:
|
||||
|
||||
24
package.json
24
package.json
@@ -1,17 +1,17 @@
|
||||
{
|
||||
"name": "snk",
|
||||
"description": "Generates a snake game from a github user contributions grid",
|
||||
"version": "2.2.0",
|
||||
"version": "2.3.0",
|
||||
"private": true,
|
||||
"repository": "github:platane/snk",
|
||||
"devDependencies": {
|
||||
"@sucrase/jest-plugin": "3.0.0",
|
||||
"@types/jest": "29.2.1",
|
||||
"@types/node": "16.11.7",
|
||||
"jest": "29.2.2",
|
||||
"prettier": "2.7.1",
|
||||
"sucrase": "3.28.0",
|
||||
"typescript": "4.8.4"
|
||||
"@types/jest": "29.5.3",
|
||||
"@types/node": "16.18.38",
|
||||
"jest": "29.6.1",
|
||||
"prettier": "2.8.8",
|
||||
"sucrase": "3.33.0",
|
||||
"typescript": "5.1.6"
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/**"
|
||||
@@ -27,10 +27,10 @@
|
||||
},
|
||||
"scripts": {
|
||||
"type": "tsc --noEmit",
|
||||
"lint": "yarn prettier -c '**/*.{ts,js,json,md,yml,yaml}' '!packages/*/dist/**' '!svg-only/dist/**'",
|
||||
"test": "jest --verbose --passWithNoTests --no-cache",
|
||||
"dev:demo": "( cd packages/demo ; yarn dev )",
|
||||
"build:demo": "( cd packages/demo ; yarn build )",
|
||||
"build:action": "( cd packages/action ; yarn build )"
|
||||
"lint": "prettier -c '**/*.{ts,js,json,md,yml,yaml}' '!packages/*/dist/**' '!svg-only/dist/**'",
|
||||
"test": "jest --verbose --no-cache",
|
||||
"dev:demo": "( cd packages/demo ; npm run dev )",
|
||||
"build:demo": "( cd packages/demo ; npm run build )",
|
||||
"build:action": "( cd packages/action ; npm run build )"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,81 +1,5 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`should parse /out.svg {"color_snake":"yellow"} 1`] = `
|
||||
{
|
||||
"animationOptions": {
|
||||
"frameDuration": 100,
|
||||
"step": 1,
|
||||
},
|
||||
"drawOptions": {
|
||||
"colorDotBorder": "#1b1f230a",
|
||||
"colorDots": [
|
||||
"#ebedf0",
|
||||
"#9be9a8",
|
||||
"#40c463",
|
||||
"#30a14e",
|
||||
"#216e39",
|
||||
],
|
||||
"colorEmpty": "#ebedf0",
|
||||
"colorSnake": "yellow",
|
||||
"dark": {
|
||||
"colorDotBorder": "#1b1f230a",
|
||||
"colorDots": [
|
||||
"#161b22",
|
||||
"#01311f",
|
||||
"#034525",
|
||||
"#0f6d31",
|
||||
"#00c647",
|
||||
],
|
||||
"colorEmpty": "#161b22",
|
||||
"colorSnake": "purple",
|
||||
},
|
||||
"sizeCell": 16,
|
||||
"sizeDot": 12,
|
||||
"sizeDotBorderRadius": 2,
|
||||
},
|
||||
"filename": "/out.svg",
|
||||
"format": "svg",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`should parse /out.svg?.gif.svg?color_snake=orange 1`] = `
|
||||
{
|
||||
"animationOptions": {
|
||||
"frameDuration": 100,
|
||||
"step": 1,
|
||||
},
|
||||
"drawOptions": {
|
||||
"colorDotBorder": "#1b1f230a",
|
||||
"colorDots": [
|
||||
"#ebedf0",
|
||||
"#9be9a8",
|
||||
"#40c463",
|
||||
"#30a14e",
|
||||
"#216e39",
|
||||
],
|
||||
"colorEmpty": "#ebedf0",
|
||||
"colorSnake": "orange",
|
||||
"dark": {
|
||||
"colorDotBorder": "#1b1f230a",
|
||||
"colorDots": [
|
||||
"#161b22",
|
||||
"#01311f",
|
||||
"#034525",
|
||||
"#0f6d31",
|
||||
"#00c647",
|
||||
],
|
||||
"colorEmpty": "#161b22",
|
||||
"colorSnake": "purple",
|
||||
},
|
||||
"sizeCell": 16,
|
||||
"sizeDot": 12,
|
||||
"sizeDotBorderRadius": 2,
|
||||
},
|
||||
"filename": "/out.svg?.gif.svg",
|
||||
"format": "svg",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`should parse /out.svg?{"color_snake":"yellow","color_dots":["#000","#111","#222","#333","#444"]} 1`] = `
|
||||
{
|
||||
"animationOptions": {
|
||||
@@ -148,6 +72,7 @@ exports[`should parse /out.svg?color_snake=orange&color_dots=#000,#111,#222,#333
|
||||
"colorEmpty": "#000",
|
||||
"colorSnake": "orange",
|
||||
"dark": {
|
||||
"colorDotBorder": "#1b1f230a",
|
||||
"colorDots": [
|
||||
"#a00",
|
||||
"#a11",
|
||||
@@ -156,6 +81,7 @@ exports[`should parse /out.svg?color_snake=orange&color_dots=#000,#111,#222,#333
|
||||
"#a44",
|
||||
],
|
||||
"colorEmpty": "#a00",
|
||||
"colorSnake": "orange",
|
||||
},
|
||||
"sizeCell": 16,
|
||||
"sizeDot": 12,
|
||||
@@ -183,18 +109,7 @@ exports[`should parse path/to/out.gif 1`] = `
|
||||
],
|
||||
"colorEmpty": "#ebedf0",
|
||||
"colorSnake": "purple",
|
||||
"dark": {
|
||||
"colorDotBorder": "#1b1f230a",
|
||||
"colorDots": [
|
||||
"#161b22",
|
||||
"#01311f",
|
||||
"#034525",
|
||||
"#0f6d31",
|
||||
"#00c647",
|
||||
],
|
||||
"colorEmpty": "#161b22",
|
||||
"colorSnake": "purple",
|
||||
},
|
||||
"dark": undefined,
|
||||
"sizeCell": 16,
|
||||
"sizeDot": 12,
|
||||
"sizeDotBorderRadius": 2,
|
||||
|
||||
@@ -1,17 +1,58 @@
|
||||
import { parseEntry } from "../outputsOptions";
|
||||
|
||||
it("should parse options as json", () => {
|
||||
expect(
|
||||
parseEntry(`/out.svg {"color_snake":"yellow"}`)?.drawOptions
|
||||
).toHaveProperty("colorSnake", "yellow");
|
||||
|
||||
expect(
|
||||
parseEntry(`/out.svg?{"color_snake":"yellow"}`)?.drawOptions
|
||||
).toHaveProperty("colorSnake", "yellow");
|
||||
|
||||
expect(
|
||||
parseEntry(`/out.svg?{"color_dots":["#000","#111","#222","#333","#444"]}`)
|
||||
?.drawOptions.colorDots
|
||||
).toEqual(["#000", "#111", "#222", "#333", "#444"]);
|
||||
});
|
||||
|
||||
it("should parse options as searchparams", () => {
|
||||
expect(parseEntry(`/out.svg?color_snake=yellow`)?.drawOptions).toHaveProperty(
|
||||
"colorSnake",
|
||||
"yellow"
|
||||
);
|
||||
|
||||
expect(
|
||||
parseEntry(`/out.svg?color_dots=#000,#111,#222,#333,#444`)?.drawOptions
|
||||
.colorDots
|
||||
).toEqual(["#000", "#111", "#222", "#333", "#444"]);
|
||||
});
|
||||
|
||||
it("should parse filename", () => {
|
||||
expect(parseEntry(`/a/b/c.svg?{"color_snake":"yellow"}`)).toHaveProperty(
|
||||
"filename",
|
||||
"/a/b/c.svg"
|
||||
);
|
||||
expect(
|
||||
parseEntry(`/a/b/out.svg?.gif.svg?{"color_snake":"yellow"}`)
|
||||
).toHaveProperty("filename", "/a/b/out.svg?.gif.svg");
|
||||
|
||||
expect(
|
||||
parseEntry(`/a/b/{[-1].svg?.gif.svg?{"color_snake":"yellow"}`)
|
||||
).toHaveProperty("filename", "/a/b/{[-1].svg?.gif.svg");
|
||||
});
|
||||
|
||||
[
|
||||
// default
|
||||
"path/to/out.gif",
|
||||
|
||||
// overwrite colors (search params)
|
||||
"/out.svg?color_snake=orange&color_dots=#000,#111,#222,#333,#444",
|
||||
|
||||
// overwrite colors (json)
|
||||
`/out.svg?{"color_snake":"yellow","color_dots":["#000","#111","#222","#333","#444"]}`,
|
||||
|
||||
`/out.svg {"color_snake":"yellow"}`,
|
||||
|
||||
// overwrite dark colors
|
||||
"/out.svg?color_snake=orange&color_dots=#000,#111,#222,#333,#444&dark_color_dots=#a00,#a11,#a22,#a33,#a44",
|
||||
|
||||
"/out.svg?.gif.svg?color_snake=orange",
|
||||
].forEach((entry) =>
|
||||
it(`should parse ${entry}`, () => {
|
||||
expect(parseEntry(entry)).toMatchSnapshot();
|
||||
|
||||
@@ -32,6 +32,7 @@ export const parseEntry = (entry: string) => {
|
||||
sizeCell: 16,
|
||||
sizeDot: 12,
|
||||
...palettes["default"],
|
||||
dark: palettes["default"].dark && { ...palettes["default"].dark },
|
||||
};
|
||||
const animationOptions: AnimationOptions = { step: 1, frameDuration: 100 };
|
||||
|
||||
@@ -43,6 +44,14 @@ export const parseEntry = (entry: string) => {
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const dark_palette = palettes[sp.get("dark_palette")!];
|
||||
if (dark_palette) {
|
||||
const clone = { ...dark_palette, dark: undefined };
|
||||
drawOptions.dark = clone;
|
||||
}
|
||||
}
|
||||
|
||||
if (sp.has("color_snake")) drawOptions.colorSnake = sp.get("color_snake")!;
|
||||
if (sp.has("color_dots")) {
|
||||
const colors = sp.get("color_dots")!.split(/[,;]/);
|
||||
@@ -56,6 +65,8 @@ export const parseEntry = (entry: string) => {
|
||||
if (sp.has("dark_color_dots")) {
|
||||
const colors = sp.get("dark_color_dots")!.split(/[,;]/);
|
||||
drawOptions.dark = {
|
||||
colorDotBorder: drawOptions.colorDotBorder,
|
||||
colorSnake: drawOptions.colorSnake,
|
||||
...drawOptions.dark,
|
||||
colorDots: colors,
|
||||
colorEmpty: colors[0],
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"@snk/types": "1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vercel/ncc": "0.34.0"
|
||||
"@vercel/ncc": "0.36.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "ncc build --external canvas --external gifsicle --out dist ./index.ts",
|
||||
|
||||
@@ -23,8 +23,5 @@ export const basePalettes: Record<
|
||||
|
||||
// aliases
|
||||
export const palettes = { ...basePalettes };
|
||||
palettes["github"] = {
|
||||
...palettes["github-light"],
|
||||
dark: { ...palettes["github-dark"] },
|
||||
};
|
||||
palettes["github"] = palettes["github-light"];
|
||||
palettes["default"] = palettes["github"];
|
||||
|
||||
@@ -10,14 +10,14 @@
|
||||
"@snk/types": "1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/dat.gui": "0.7.7",
|
||||
"@types/dat.gui": "0.7.10",
|
||||
"dat.gui": "0.7.9",
|
||||
"html-webpack-plugin": "5.5.0",
|
||||
"ts-loader": "9.4.1",
|
||||
"html-webpack-plugin": "5.5.3",
|
||||
"ts-loader": "9.4.4",
|
||||
"ts-node": "10.9.1",
|
||||
"webpack": "5.74.0",
|
||||
"webpack-cli": "4.10.0",
|
||||
"webpack-dev-server": "4.11.1"
|
||||
"webpack": "5.88.1",
|
||||
"webpack-cli": "5.1.4",
|
||||
"webpack-dev-server": "4.15.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "webpack",
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"devDependencies": {
|
||||
"@types/gifsicle": "5.2.0",
|
||||
"@types/tmp": "0.2.3",
|
||||
"@vercel/ncc": "0.34.0"
|
||||
"@vercel/ncc": "0.36.1"
|
||||
},
|
||||
"scripts": {
|
||||
"benchmark": "ncc run __tests__/benchmark.ts --quiet"
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@snk/github-user-contribution": "1.0.0",
|
||||
"@vercel/node": "2.6.1"
|
||||
"@vercel/node": "2.15.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,40 +39,36 @@ export const getGithubUserContribution = async (
|
||||
};
|
||||
|
||||
const parseUserPage = (content: string) => {
|
||||
// take roughly the svg block
|
||||
// take roughly the table block
|
||||
const block = content
|
||||
.split(`class="js-calendar-graph-svg"`)[1]
|
||||
.split("</svg>")[0];
|
||||
.split(`aria-describedby="contribution-graph-description"`)[1]
|
||||
.split("<tbody>")[1]
|
||||
.split("</tbody>")[0];
|
||||
|
||||
let x = 0;
|
||||
let lastYAttribute = 0;
|
||||
const cells = block.split("</tr>").flatMap((inside, y) =>
|
||||
inside.split("</td>").flatMap((m) => {
|
||||
const date = m.match(/data-date="([^"]+)"/)?.[1];
|
||||
|
||||
const rects = Array.from(block.matchAll(/<rect[^>]*>[^<]*<\/rect>/g)).map(
|
||||
([m]) => {
|
||||
const date = m.match(/data-date="([^"]+)"/)![1];
|
||||
const level = +m.match(/data-level="([^"]+)"/)![1];
|
||||
const yAttribute = +m.match(/y="([^"]+)"/)![1];
|
||||
const literalLevel = m.match(/data-level="([^"]+)"/)?.[1];
|
||||
const literalX = m.match(/data-ix="([^"]+)"/)?.[1];
|
||||
const literalCount = m.match(/(No|\d+) contributions? on/)?.[1];
|
||||
|
||||
const literalCount = m.match(/(No|\d+) contributions? on/)![1];
|
||||
const count = literalCount === "No" ? 0 : +literalCount;
|
||||
if (date && literalLevel && literalX && literalCount)
|
||||
return [
|
||||
{
|
||||
x: +literalX,
|
||||
y,
|
||||
|
||||
if (lastYAttribute > yAttribute) x++;
|
||||
date,
|
||||
count: +literalCount,
|
||||
level: +literalLevel,
|
||||
},
|
||||
];
|
||||
|
||||
lastYAttribute = yAttribute;
|
||||
|
||||
return { date, count, level, x, yAttribute };
|
||||
}
|
||||
return [];
|
||||
})
|
||||
);
|
||||
|
||||
const yAttributes = Array.from(
|
||||
new Set(rects.map((c) => c.yAttribute)).keys()
|
||||
).sort();
|
||||
|
||||
const cells = rects.map(({ yAttribute, ...c }) => ({
|
||||
y: yAttributes.indexOf(yAttribute),
|
||||
...c,
|
||||
}));
|
||||
|
||||
return cells;
|
||||
};
|
||||
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
"name": "@snk/github-user-contribution",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"cheerio": "1.0.0-rc.10",
|
||||
"node-fetch": "2.6.7"
|
||||
"node-fetch": "2.6.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node-fetch": "2.6.1"
|
||||
"@types/node-fetch": "2.6.2"
|
||||
}
|
||||
}
|
||||
|
||||
97
svg-only/dist/197.index.js
vendored
97
svg-only/dist/197.index.js
vendored
@@ -1425,6 +1425,20 @@ const isDomainOrSubdomain = function isDomainOrSubdomain(destination, original)
|
||||
return orig === dest || orig[orig.length - dest.length - 1] === '.' && orig.endsWith(dest);
|
||||
};
|
||||
|
||||
/**
|
||||
* isSameProtocol reports whether the two provided URLs use the same protocol.
|
||||
*
|
||||
* Both domains must already be in canonical form.
|
||||
* @param {string|URL} original
|
||||
* @param {string|URL} destination
|
||||
*/
|
||||
const isSameProtocol = function isSameProtocol(destination, original) {
|
||||
const orig = new URL$1(original).protocol;
|
||||
const dest = new URL$1(destination).protocol;
|
||||
|
||||
return orig === dest;
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch function
|
||||
*
|
||||
@@ -1456,7 +1470,7 @@ function fetch(url, opts) {
|
||||
let error = new AbortError('The user aborted a request.');
|
||||
reject(error);
|
||||
if (request.body && request.body instanceof Stream.Readable) {
|
||||
request.body.destroy(error);
|
||||
destroyStream(request.body, error);
|
||||
}
|
||||
if (!response || !response.body) return;
|
||||
response.body.emit('error', error);
|
||||
@@ -1497,9 +1511,43 @@ function fetch(url, opts) {
|
||||
|
||||
req.on('error', function (err) {
|
||||
reject(new FetchError(`request to ${request.url} failed, reason: ${err.message}`, 'system', err));
|
||||
|
||||
if (response && response.body) {
|
||||
destroyStream(response.body, err);
|
||||
}
|
||||
|
||||
finalize();
|
||||
});
|
||||
|
||||
fixResponseChunkedTransferBadEnding(req, function (err) {
|
||||
if (signal && signal.aborted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (response && response.body) {
|
||||
destroyStream(response.body, err);
|
||||
}
|
||||
});
|
||||
|
||||
/* c8 ignore next 18 */
|
||||
if (parseInt(process.version.substring(1)) < 14) {
|
||||
// Before Node.js 14, pipeline() does not fully support async iterators and does not always
|
||||
// properly handle when the socket close/end events are out of order.
|
||||
req.on('socket', function (s) {
|
||||
s.addListener('close', function (hadError) {
|
||||
// if a data listener is still present we didn't end cleanly
|
||||
const hasDataListener = s.listenerCount('data') > 0;
|
||||
|
||||
// if end happened before close but the socket didn't emit an error, do it now
|
||||
if (response && hasDataListener && !hadError && !(signal && signal.aborted)) {
|
||||
const err = new Error('Premature close');
|
||||
err.code = 'ERR_STREAM_PREMATURE_CLOSE';
|
||||
response.body.emit('error', err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
req.on('response', function (res) {
|
||||
clearTimeout(reqTimeout);
|
||||
|
||||
@@ -1571,7 +1619,7 @@ function fetch(url, opts) {
|
||||
size: request.size
|
||||
};
|
||||
|
||||
if (!isDomainOrSubdomain(request.url, locationURL)) {
|
||||
if (!isDomainOrSubdomain(request.url, locationURL) || !isSameProtocol(request.url, locationURL)) {
|
||||
for (const name of ['authorization', 'www-authenticate', 'cookie', 'cookie2']) {
|
||||
requestOpts.headers.delete(name);
|
||||
}
|
||||
@@ -1664,6 +1712,13 @@ function fetch(url, opts) {
|
||||
response = new Response(body, response_options);
|
||||
resolve(response);
|
||||
});
|
||||
raw.on('end', function () {
|
||||
// some old IIS servers return zero-length OK deflate responses, so 'data' is never emitted.
|
||||
if (!response) {
|
||||
response = new Response(body, response_options);
|
||||
resolve(response);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1683,6 +1738,44 @@ function fetch(url, opts) {
|
||||
writeToStream(req, request);
|
||||
});
|
||||
}
|
||||
function fixResponseChunkedTransferBadEnding(request, errorCallback) {
|
||||
let socket;
|
||||
|
||||
request.on('socket', function (s) {
|
||||
socket = s;
|
||||
});
|
||||
|
||||
request.on('response', function (response) {
|
||||
const headers = response.headers;
|
||||
|
||||
if (headers['transfer-encoding'] === 'chunked' && !headers['content-length']) {
|
||||
response.once('close', function (hadError) {
|
||||
// tests for socket presence, as in some situations the
|
||||
// the 'socket' event is not triggered for the request
|
||||
// (happens in deno), avoids `TypeError`
|
||||
// if a data listener is still present we didn't end cleanly
|
||||
const hasDataListener = socket && socket.listenerCount('data') > 0;
|
||||
|
||||
if (hasDataListener && !hadError) {
|
||||
const err = new Error('Premature close');
|
||||
err.code = 'ERR_STREAM_PREMATURE_CLOSE';
|
||||
errorCallback(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function destroyStream(stream, err) {
|
||||
if (stream.destroy) {
|
||||
stream.destroy(err);
|
||||
} else {
|
||||
// node < 8
|
||||
stream.emit('error', err);
|
||||
stream.end();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect code matching
|
||||
*
|
||||
|
||||
58
svg-only/dist/317.index.js
vendored
58
svg-only/dist/317.index.js
vendored
@@ -78,27 +78,27 @@ const getGithubUserContribution = async (userName, options = {}) => {
|
||||
return parseUserPage(resText);
|
||||
};
|
||||
const parseUserPage = (content) => {
|
||||
// take roughly the svg block
|
||||
// take roughly the table block
|
||||
const block = content
|
||||
.split(`class="js-calendar-graph-svg"`)[1]
|
||||
.split("</svg>")[0];
|
||||
let x = 0;
|
||||
let lastYAttribute = 0;
|
||||
const rects = Array.from(block.matchAll(/<rect[^>]*>[^<]*<\/rect>/g)).map(([m]) => {
|
||||
const date = m.match(/data-date="([^"]+)"/)[1];
|
||||
const level = +m.match(/data-level="([^"]+)"/)[1];
|
||||
const yAttribute = +m.match(/y="([^"]+)"/)[1];
|
||||
const literalCount = m.match(/(No|\d+) contributions? on/)[1];
|
||||
const count = literalCount === "No" ? 0 : +literalCount;
|
||||
if (lastYAttribute > yAttribute)
|
||||
x++;
|
||||
lastYAttribute = yAttribute;
|
||||
return { date, count, level, x, yAttribute };
|
||||
});
|
||||
const yAttributes = Array.from(new Set(rects.map((c) => c.yAttribute)).keys()).sort();
|
||||
const cells = rects.map(({ yAttribute, ...c }) => ({
|
||||
y: yAttributes.indexOf(yAttribute),
|
||||
...c,
|
||||
.split(`aria-describedby="contribution-graph-description"`)[1]
|
||||
.split("<tbody>")[1]
|
||||
.split("</tbody>")[0];
|
||||
const cells = block.split("</tr>").flatMap((inside, y) => inside.split("</td>").flatMap((m) => {
|
||||
const date = m.match(/data-date="([^"]+)"/)?.[1];
|
||||
const literalLevel = m.match(/data-level="([^"]+)"/)?.[1];
|
||||
const literalX = m.match(/data-ix="([^"]+)"/)?.[1];
|
||||
const literalCount = m.match(/(No|\d+) contributions? on/)?.[1];
|
||||
if (date && literalLevel && literalX && literalCount)
|
||||
return [
|
||||
{
|
||||
x: +literalX,
|
||||
y,
|
||||
date,
|
||||
count: +literalCount,
|
||||
level: +literalLevel,
|
||||
},
|
||||
];
|
||||
return [];
|
||||
}));
|
||||
return cells;
|
||||
};
|
||||
@@ -689,14 +689,14 @@ const generateContributionSnake = async (userName, outputs) => {
|
||||
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "V0": () => (/* binding */ isInside),
|
||||
/* harmony export */ "HJ": () => (/* binding */ isInsideLarge),
|
||||
/* harmony export */ "VJ": () => (/* binding */ copyGrid),
|
||||
/* harmony export */ "Lq": () => (/* binding */ getColor),
|
||||
/* harmony export */ "xb": () => (/* binding */ isEmpty),
|
||||
/* harmony export */ "vk": () => (/* binding */ setColor),
|
||||
/* harmony export */ "Dy": () => (/* binding */ setColorEmpty),
|
||||
/* harmony export */ "u1": () => (/* binding */ createEmptyGrid)
|
||||
/* harmony export */ "HJ": () => (/* binding */ isInsideLarge),
|
||||
/* harmony export */ "Lq": () => (/* binding */ getColor),
|
||||
/* harmony export */ "V0": () => (/* binding */ isInside),
|
||||
/* harmony export */ "VJ": () => (/* binding */ copyGrid),
|
||||
/* harmony export */ "u1": () => (/* binding */ createEmptyGrid),
|
||||
/* harmony export */ "vk": () => (/* binding */ setColor),
|
||||
/* harmony export */ "xb": () => (/* binding */ isEmpty)
|
||||
/* harmony export */ });
|
||||
/* unused harmony exports isGridEmpty, gridEquals */
|
||||
const isInside = (grid, x, y) => x >= 0 && y >= 0 && x < grid.width && y < grid.height;
|
||||
@@ -733,13 +733,13 @@ const createEmptyGrid = (width, height) => ({
|
||||
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "If": () => (/* binding */ getHeadX),
|
||||
/* harmony export */ "IP": () => (/* binding */ getHeadY),
|
||||
/* harmony export */ "If": () => (/* binding */ getHeadX),
|
||||
/* harmony export */ "JJ": () => (/* binding */ getSnakeLength),
|
||||
/* harmony export */ "Ks": () => (/* binding */ snakeToCells),
|
||||
/* harmony export */ "kE": () => (/* binding */ snakeEquals),
|
||||
/* harmony export */ "kv": () => (/* binding */ nextSnake),
|
||||
/* harmony export */ "nJ": () => (/* binding */ snakeWillSelfCollide),
|
||||
/* harmony export */ "Ks": () => (/* binding */ snakeToCells),
|
||||
/* harmony export */ "xG": () => (/* binding */ createSnakeFromCells)
|
||||
/* harmony export */ });
|
||||
/* unused harmony export copySnake */
|
||||
|
||||
18
svg-only/dist/index.js
vendored
18
svg-only/dist/index.js
vendored
@@ -2991,7 +2991,7 @@ var external_path_ = __nccwpck_require__(1017);
|
||||
// EXTERNAL MODULE: ../../node_modules/@actions/core/lib/core.js
|
||||
var core = __nccwpck_require__(7117);
|
||||
;// CONCATENATED MODULE: ./palettes.ts
|
||||
const palettes = {
|
||||
const basePalettes = {
|
||||
"github-light": {
|
||||
colorDotBorder: "#1b1f230a",
|
||||
colorDots: ["#ebedf0", "#9be9a8", "#40c463", "#30a14e", "#216e39"],
|
||||
@@ -3006,10 +3006,8 @@ const palettes = {
|
||||
},
|
||||
};
|
||||
// aliases
|
||||
palettes["github"] = {
|
||||
...palettes["github-light"],
|
||||
dark: { ...palettes["github-dark"] },
|
||||
};
|
||||
const palettes = { ...basePalettes };
|
||||
palettes["github"] = palettes["github-light"];
|
||||
palettes["default"] = palettes["github"];
|
||||
|
||||
;// CONCATENATED MODULE: ./outputsOptions.ts
|
||||
@@ -3039,6 +3037,7 @@ const parseEntry = (entry) => {
|
||||
sizeCell: 16,
|
||||
sizeDot: 12,
|
||||
...palettes["default"],
|
||||
dark: palettes["default"].dark && { ...palettes["default"].dark },
|
||||
};
|
||||
const animationOptions = { step: 1, frameDuration: 100 };
|
||||
{
|
||||
@@ -3048,6 +3047,13 @@ const parseEntry = (entry) => {
|
||||
drawOptions.dark = palette.dark && { ...palette.dark };
|
||||
}
|
||||
}
|
||||
{
|
||||
const dark_palette = palettes[sp.get("dark_palette")];
|
||||
if (dark_palette) {
|
||||
const clone = { ...dark_palette, dark: undefined };
|
||||
drawOptions.dark = clone;
|
||||
}
|
||||
}
|
||||
if (sp.has("color_snake"))
|
||||
drawOptions.colorSnake = sp.get("color_snake");
|
||||
if (sp.has("color_dots")) {
|
||||
@@ -3061,6 +3067,8 @@ const parseEntry = (entry) => {
|
||||
if (sp.has("dark_color_dots")) {
|
||||
const colors = sp.get("dark_color_dots").split(/[,;]/);
|
||||
drawOptions.dark = {
|
||||
colorDotBorder: drawOptions.colorDotBorder,
|
||||
colorSnake: drawOptions.colorSnake,
|
||||
...drawOptions.dark,
|
||||
colorDots: colors,
|
||||
colorEmpty: colors[0],
|
||||
|
||||
Reference in New Issue
Block a user