Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor #30

Merged
merged 7 commits into from
Jun 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .changeset/pre.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"mode": "exit",
"tag": "refactor",
"initialVersions": {
"@example/nextjs": "0.0.0",
"@example/remix": "0.0.0",
"@example/vite": "0.0.0",
"r18gs": "1.1.2",
"@repo/eslint-config": "0.0.0",
"@repo/typescript-config": "0.0.0",
"@repo/shared": "0.0.1",
"@repo/scripts": "0.0.0"
},
"changesets": [
"twenty-starfishes-unite"
]
}
5 changes: 5 additions & 0 deletions .changeset/twenty-starfishes-unite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"r18gs": patch
---

Simplify build - handle prototype pollution vulnerability.
2 changes: 0 additions & 2 deletions contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ This TurboRepo comprises the following packages/examples, all written in [TypeSc
- `@example/remix`: a Remix app
- `@repo/eslint-config`: `eslint` configurations (includes `eslint-config-next` and `eslint-config-prettier`)
- `@repo/typescript-config`: `tsconfig.json`s used throughout the monorepo
- `@repo/jest-presets`: Jest presets for unit testing
- `@repo/logger`: A configurable shared logger utility
- `@repo/shared`: An internal library of components utilized by the examples
- `react18-loaders`: a React component library (The core package published to NPM)

Expand Down
3 changes: 1 addition & 2 deletions examples/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
"start": "next start"
},
"dependencies": {
"@repo/logger": "workspace:*",
"@repo/shared": "workspace:*",
"next": "^14.2.4",
"nextjs-darkmode": "^1.0.4",
Expand All @@ -30,4 +29,4 @@
"@types/react-dom": "^18.3.0",
"typescript": "^5.5.2"
}
}
}
6 changes: 6 additions & 0 deletions lib/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# r18gs

## 1.1.3-refactor.0

### Patch Changes

- 07e654a: Simplify build - handle prototype pollution vulnerability.

## 1.1.2

### Patch Changes
Expand Down
3 changes: 1 addition & 2 deletions lib/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "r18gs",
"author": "Mayank Kumar Chaudhari <https://mayank-chaudhari.vercel.app>",
"private": false,
"version": "1.1.2",
"version": "1.1.3-refactor.0",
"description": "A simple yet elegant, light weight, react18 global store to replace Zustand for better tree shaking.",
"license": "MPL-2.0",
"main": "./dist/index.js",
Expand All @@ -25,7 +25,6 @@
},
"devDependencies": {
"@repo/eslint-config": "workspace:*",
"@repo/jest-presets": "workspace:*",
"@repo/typescript-config": "workspace:*",
"@testing-library/react": "^16.0.0",
"@types/node": "^20.14.8",
Expand Down
12 changes: 6 additions & 6 deletions lib/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ export type { SetterArgType, SetStateAction, Plugin } from "./utils";
const useRGS = <T>(key: string, value?: ValueType<T>): [T, SetStateAction<T>] => {
/** Initialize the named store when invoked for the first time. */
if (!globalRGS[key])
globalRGS[key] = [
globalRGS[key] = {
// @ts-expect-error -- ok
typeof value === "function" ? value() : value,
[],
createSetter(key),
createSubcriber(key),
];
v: typeof value === "function" ? value() : value,
l: [],
s: createSetter(key),
u: createSubcriber(key),
};

return createHook<T>(key);
};
Expand Down
38 changes: 18 additions & 20 deletions lib/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ export type ValueType<T> = T | (() => T);
/**
* This is a hack to reduce lib size + readability + not encouraging direct access to globalThis
*/
const [VALUE, LISTENERS, SETTER, SUBSCRIBER] = [0, 1, 2, 3, 4];
type RGS = [unknown, Listener[], SetStateAction<unknown> | null, Subscriber];
type RGS = { v: unknown; l: Listener[]; s: SetStateAction<unknown> | null; u: Subscriber };

declare global {
// eslint-disable-next-line no-var -- var required for global declaration.
Expand All @@ -24,16 +23,15 @@ globalThisForBetterMinification.rgs = {};
export const globalRGS = globalThisForBetterMinification.rgs;

/** trigger all listeners */
const triggerListeners = (rgs: RGS) =>
(rgs[LISTENERS] as Listener[]).forEach(listener => listener());
const triggerListeners = (rgs: RGS) => rgs.l.forEach(listener => listener());

/** craete subscriber function to subscribe to the store. */
export const createSubcriber = (key: string): Subscriber => {
return listener => {
const rgs = globalRGS[key] as RGS;
(rgs[LISTENERS] as Listener[]).push(listener);
(rgs.l as Listener[]).push(listener);
return () => {
rgs[LISTENERS] = (rgs[LISTENERS] as Listener[]).filter(l => l !== listener);
rgs.l = (rgs.l as Listener[]).filter(l => l !== listener);
};
};
};
Expand All @@ -42,18 +40,18 @@ export const createSubcriber = (key: string): Subscriber => {
export const createSetter = <T>(key: string): SetStateAction<unknown> => {
return val => {
const rgs = globalRGS[key] as RGS;
rgs[VALUE] = typeof val === "function" ? val(rgs[VALUE] as T) : val;
(rgs[LISTENERS] as Listener[]).forEach(listener => listener());
rgs.v = typeof val === "function" ? val(rgs.v as T) : val;
(rgs.l as Listener[]).forEach(listener => listener());
};
};

/** Extract coomon create hook logic to utils */
export const createHook = <T>(key: string): [T, SetStateAction<T>] => {
const rgs = globalRGS[key] as RGS;
/** This function is called by react to get the current stored value. */
const getSnapshot = () => rgs[VALUE] as T;
const val = useSyncExternalStore<T>(rgs[SUBSCRIBER] as Subscriber, getSnapshot, getSnapshot);
return [val, rgs[SETTER] as SetStateAction<T>];
const getSnapshot = () => rgs.v as T;
const val = useSyncExternalStore<T>(rgs.u as Subscriber, getSnapshot, getSnapshot);
return [val, rgs.s as SetStateAction<T>];
};

type Mutate<T> = (value?: T) => void;
Expand All @@ -69,12 +67,12 @@ const initPlugins = async <T>(key: string, plugins: Plugin<T>[]) => {
const rgs = globalRGS[key] as RGS;
/** Mutate function to update the value */
const mutate: Mutate<T> = newValue => {
rgs[VALUE] = newValue;
rgs.v = newValue;
triggerListeners(rgs);
};
for (const plugin of plugins) {
/** Next plugins initializer will get the new value if updated by previous one */
await plugin.init?.(key, rgs[VALUE] as T, mutate);
await plugin.init?.(key, rgs.v as T, mutate);
}
allExtentionsInitialized = true;
};
Expand All @@ -89,24 +87,24 @@ export const initWithPlugins = <T>(
value = value instanceof Function ? value() : value;
if (doNotInit) {
/** You will not have access to the setter until initialized */
globalRGS[key] = [value, [], null, createSubcriber(key)];
globalRGS[key] = { v: value, l: [], s: null, u: createSubcriber(key) };
return;
}
/** setter function to set the state. */
const setterWithPlugins: SetStateAction<unknown> = val => {
/** Do not allow mutating the store before all extentions are initialized */
if (!allExtentionsInitialized) return;
const rgs = globalRGS[key] as RGS;
rgs[VALUE] = val instanceof Function ? val(rgs[VALUE] as T) : val;
rgs.v = val instanceof Function ? val(rgs.v as T) : val;
triggerListeners(rgs);
plugins.forEach(plugin => plugin.onChange?.(key, rgs[VALUE] as T));
plugins.forEach(plugin => plugin.onChange?.(key, rgs.v as T));
};

const rgs = globalRGS[key];
if (rgs) {
rgs[VALUE] = value;
rgs[SETTER] = setterWithPlugins;
} else globalRGS[key] = [value, [], setterWithPlugins, createSubcriber(key)];
rgs.v = value;
rgs.s = setterWithPlugins;
} else globalRGS[key] = { v: value, l: [], s: setterWithPlugins, u: createSubcriber(key) };
initPlugins(key, plugins);
};

Expand All @@ -133,6 +131,6 @@ export const useRGSWithPlugins = <T>(
plugins?: Plugin<T>[],
doNotInit = false,
): [T, SetStateAction<T>] => {
if (!globalRGS[key]?.[SETTER]) initWithPlugins(key, value, plugins, doNotInit);
if (!globalRGS[key]?.s) initWithPlugins(key, value, plugins, doNotInit);
return createHook<T>(key);
};
33 changes: 0 additions & 33 deletions lib/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { defineConfig } from "tsup";
import fs from "node:fs";

export default defineConfig(options => ({
format: ["cjs", "esm"],
Expand All @@ -9,36 +8,4 @@ export default defineConfig(options => ({
clean: !options.watch,
bundle: true,
minify: !options.watch,
esbuildPlugins: [
{
name: "improve-minify",
setup(build) {
build.onLoad({ filter: /utils.ts/ }, args => {
let contents = fs.readFileSync(args.path, "utf8");
const lines = contents.split("\n");
const hackLine = lines.find(line => line.startsWith("const [VALUE,"));

if (!hackLine) return { contents, loader: "ts" };
/** remove hackLine */
contents = contents.replace(hackLine, "");

/** clean up */
const tokens = hackLine
.replace("const", "")
.split("=")[0]
.trim()
.replace(/\]|\[/g, "")
.split(",")
.map((t, i) => ({ token: t.trim(), i }));

tokens.sort((a, b) => b.token.length - a.token.length);

for (const t of tokens) {
contents = contents.replace(new RegExp(`${t.token}`, "g"), String(t.i));
}
return { contents, loader: "ts" };
});
},
},
],
}));
3 changes: 0 additions & 3 deletions packages/config-eslint/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ module.exports = {
overrides: [
{
files: ["**/__tests__/**/*"],
env: {
jest: true,
},
},
],
ignorePatterns: [".*.js", "node_modules/", "dist/"],
Expand Down
14 changes: 0 additions & 14 deletions packages/jest-presets/browser/jest-preset.js

This file was deleted.

13 changes: 0 additions & 13 deletions packages/jest-presets/node/jest-preset.js

This file was deleted.

16 changes: 0 additions & 16 deletions packages/jest-presets/package.json

This file was deleted.

11 changes: 0 additions & 11 deletions packages/logger/.eslintrc.js

This file was deleted.

30 changes: 0 additions & 30 deletions packages/logger/package.json

This file was deleted.

11 changes: 0 additions & 11 deletions packages/logger/src/__tests__/log.test.ts

This file was deleted.

4 changes: 0 additions & 4 deletions packages/logger/src/index.ts

This file was deleted.

10 changes: 0 additions & 10 deletions packages/logger/tsconfig.json

This file was deleted.

9 changes: 0 additions & 9 deletions packages/logger/tsup.config.ts

This file was deleted.

12 changes: 0 additions & 12 deletions packages/logger/turbo.json

This file was deleted.

Loading
Loading