Skip to content

Commit

Permalink
feat(@fepack/react-image): initialization
Browse files Browse the repository at this point in the history
  • Loading branch information
manudeli committed Sep 12, 2023
1 parent f83acaf commit 5528aa0
Show file tree
Hide file tree
Showing 20 changed files with 1,226 additions and 85 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"changeset": "changeset",
"changeset:publish": "pnpm prepack && changeset publish",
"changeset:version": "changeset version && pnpm i --lockfile-only",
"clean": "pnpm --filter \"./packages/**\" run clean",
"dev": "turbo run dev",
"format": "prettier --write \"**/*.{ts,tsx,md}\"",
"graph": "rimraf ./graph && mkdir graph && turbo run build --graph=graph/index.html",
Expand Down
54 changes: 54 additions & 0 deletions packages/react-image/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "@fepack/react-image",
"version": "0.0.0",
"license": "MIT",
"sideEffects": false,
"type": "module",
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
},
"./package.json": "./package.json"
},
"main": "dist/index.cjs",
"types": "dist/index.d.ts",
"files": [
"dist",
"src"
],
"author": {
"name": "Jonghyeon Ko",
"email": "[email protected]"
},
"scripts": {
"build": "tsup",
"build:watch": "tsup --watch",
"clean": "rimraf ./dist && rimraf ./coverage",
"lint": "eslint \"**/*.ts*\"",
"lint:pub": "publint --strict",
"prepack": "pnpm build",
"type:check": "tsc --noEmit"
},
"devDependencies": {
"@fepack/tsconfig": "workspace:*",
"@types/node": "^18.16.2",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"tsup": "^7.1.0"
},
"peerDependencies": {
"react": "^16.8 || ^17 || ^18"
},
"publishConfig": {
"access": "public"
}
}
86 changes: 86 additions & 0 deletions packages/react-image/src/Load.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { FunctionComponent, createElement, useSyncExternalStore } from "react";
import { load as utilsLoad } from "./utils";

type UseLoadOptions = {
src: HTMLImageElement["src"];
};

type LoadState = {
promise?: Promise<unknown>;
src: string;
error?: unknown;
};

const loadCache = new Map<string, LoadState>();
const loadClient = new (class LoadClient {
private notifiesMap = new Map<string, ((...args: unknown[]) => unknown)[]>();

public attach(src: string, onNotify: (...args: unknown[]) => unknown) {
const keyNotifies = this.notifiesMap.get(src);
this.notifiesMap.set(src, [...(keyNotifies ?? []), onNotify]);

const attached = {
detach: () => this.detach(src, onNotify),
};
return attached;
}

public detach(src: string, onNotify: (...args: unknown[]) => unknown) {
const keyNotifies = this.notifiesMap.get(src);

if (keyNotifies) {
this.notifiesMap.set(
src,
keyNotifies.filter((notify) => notify !== onNotify),
);
}
}

public load = (src: string): { src: string } => {
const loadStateGot = loadCache.get(src);

if (loadStateGot?.error) {
// eslint-disable-next-line @typescript-eslint/no-throw-literal
throw loadStateGot.error;
}
if (loadStateGot?.src) {
return loadStateGot;
}

if (loadStateGot?.promise) {
// eslint-disable-next-line @typescript-eslint/no-throw-literal
throw loadStateGot.promise;
}

const newLoadState: LoadState = {
src,
promise: utilsLoad(src)
.then(() => {
newLoadState.src = src;
})
.catch((error) => {
newLoadState.error = error;
}),
};

loadCache.set(src, newLoadState);
// eslint-disable-next-line @typescript-eslint/no-throw-literal
throw newLoadState.promise;
};
})();

export const useLoad = (options: UseLoadOptions): { src: string } =>
useSyncExternalStore(
(onStoreChange) => loadClient.attach(options.src, onStoreChange).detach,
() => loadClient.load(options.src),
() => loadClient.load(options.src),
);

type LoadProps = {
src: HTMLImageElement["src"];
children: FunctionComponent<LoadState>;
};
export const Load = ({ src, children }: LoadProps) => {
const load = useLoad({ src });
return createElement(children, load)
};
1 change: 1 addition & 0 deletions packages/react-image/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Load, useLoad } from "./Load";
1 change: 1 addition & 0 deletions packages/react-image/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { load } from "./load";
10 changes: 10 additions & 0 deletions packages/react-image/src/utils/load.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const load = (src: HTMLImageElement["src"]): Promise<Event> => {
const image = new Image();

return new Promise((resolve, reject) => {
image.onload = (e) => resolve(e);
image.onerror = (e) => reject(e);

image.src = src;
});
};
5 changes: 5 additions & 0 deletions packages/react-image/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "@fepack/tsconfig/react-library.json",
"include": ["."],
"compilerOptions": {}
}
11 changes: 11 additions & 0 deletions packages/react-image/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { defineConfig } from 'tsup'

export default defineConfig({
banner: { js: '"use client"' },
format: ['cjs', 'esm'],
entry: ['{src,src/experimental}/*.{ts,tsx}', '!**/*.{spec,test,test-d}.*'],
sourcemap: true,
dts: true,
splitting: false,
}
)
Loading

0 comments on commit 5528aa0

Please sign in to comment.