Skip to content

Commit

Permalink
Merge pull request #30 from react18-tools/refactor
Browse files Browse the repository at this point in the history
Refactor
  • Loading branch information
mayank1513 committed Jun 22, 2024
2 parents d210d5b + bb0e257 commit e4e3de0
Show file tree
Hide file tree
Showing 23 changed files with 188 additions and 574 deletions.
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

0 comments on commit e4e3de0

Please sign in to comment.