@kirill.konshin/utils 0.0.8 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.ctirc +10 -0
- package/.storybook/main.ts +21 -0
- package/.storybook/preview.ts +17 -0
- package/.turbo/turbo-build.log +67 -0
- package/.turbo/turbo-test.log +118 -0
- package/CHANGELOG.md +6 -0
- package/README.md +59 -0
- package/builders/.swcrc +18 -0
- package/builders/build.config.ts +11 -0
- package/builders/bun.build.js +23 -0
- package/builders/package.json +209 -0
- package/builders/perf.mjs +63 -0
- package/builders/rollup.config.mjs +50 -0
- package/builders/rslib.config.ts +26 -0
- package/builders/tsconfig.json +21 -0
- package/builders/tsup.config.ts +20 -0
- package/builders/turbo.json +24 -0
- package/builders/vite.config.ts +37 -0
- package/demo/cache-demo.ts +2 -1
- package/package.json +166 -22
- package/src/bootstrap/adaptiveContainer.tsx +12 -0
- package/src/bootstrap/controls.stories.tsx +62 -0
- package/src/bootstrap/controls.tsx +119 -0
- package/src/bootstrap/error.stories.tsx +34 -0
- package/src/bootstrap/error.tsx +40 -0
- package/src/bootstrap/field.stories.tsx +42 -0
- package/src/bootstrap/field.tsx +35 -0
- package/src/bootstrap/footer.tsx +49 -0
- package/src/bootstrap/globalLoading.stories.tsx +25 -0
- package/src/bootstrap/globalLoading.tsx +17 -0
- package/src/bootstrap/index.ts +12 -0
- package/src/bootstrap/loading.stories.tsx +35 -0
- package/src/bootstrap/loading.tsx +22 -0
- package/src/bootstrap/main.scss +86 -0
- package/src/bootstrap/responsiveHelper.tsx +50 -0
- package/src/bootstrap/screen.stories.tsx +160 -0
- package/src/bootstrap/screen.tsx +144 -0
- package/src/bootstrap/toaster.stories.tsx +32 -0
- package/src/bootstrap/toaster.tsx +38 -0
- package/src/bootstrap/useModal.stories.tsx +51 -0
- package/src/bootstrap/useModal.tsx +88 -0
- package/src/bootstrap/useWrappedForm.ts +40 -0
- package/src/core/cache.test.ts +121 -0
- package/src/electron/README.md +13 -0
- package/src/electron/createWindow.ts +234 -0
- package/src/electron/index.ts +2 -0
- package/src/electron/updater.ts +54 -0
- package/src/electron-builder/builder.ts +150 -0
- package/src/electron-builder/images.ts +33 -0
- package/src/electron-builder/index.ts +2 -0
- package/src/mui/README.md +3 -0
- package/src/mui/formControlFieldset.stories.tsx +71 -0
- package/src/mui/formControlFieldset.tsx +26 -0
- package/src/mui/formLabelLegend.tsx +11 -0
- package/src/mui/genericControl.tsx +20 -0
- package/src/mui/index.ts +4 -0
- package/src/mui/readOnly.tsx +6 -0
- package/src/next/appLink.tsx +39 -0
- package/src/next/index.ts +5 -0
- package/src/next/measure.ts +7 -0
- package/src/next/noSSR.tsx +17 -0
- package/src/next/redirect.tsx +13 -0
- package/src/next/useIsInner.ts +13 -0
- package/src/react/apiCall.ts +25 -0
- package/src/react/form/client.tsx +62 -0
- package/src/react/form/form.tsx +109 -0
- package/src/react/form/index.ts +2 -0
- package/src/react/index.ts +4 -0
- package/src/react/useFetch.ts +29 -0
- package/src/react/useFetcher.ts +49 -0
- package/src/react-native/index.ts +3 -0
- package/src/react-native/share.ts +31 -0
- package/src/react-native/update.tsx +40 -0
- package/src/react-native/useAppState.ts +18 -0
- package/src/tailwind/fullpage.tsx +11 -0
- package/src/tailwind/index.ts +2 -0
- package/src/tailwind/responsiveHelper.tsx +16 -0
- package/src-todo/auth0.tsx +177 -0
- package/tsconfig.json +15 -18
- package/turbo.json +2 -11
- package/vite.config.ts +47 -0
- package/vite.exports.ts +121 -0
- package/build/cache.d.ts +0 -120
- package/build/cache.js +0 -191
- package/build/errors.d.ts +0 -1
- package/build/errors.js +0 -14
- package/build/index.d.ts +0 -5
- package/build/index.js +0 -5
- package/build/measure.d.ts +0 -34
- package/build/measure.js +0 -48
- package/build/mutex.d.ts +0 -5
- package/build/mutex.js +0 -23
- package/build/worker.d.ts +0 -83
- package/build/worker.js +0 -250
- package/tsconfig.tsbuildinfo +0 -1
- package/src/{cache.ts → core/cache.ts} +0 -0
- package/src/{errors.ts → core/errors.ts} +0 -0
- package/src/{index.ts → core/index.ts} +1 -1
- /package/src/{measure.ts → core/measure.ts} +0 -0
- /package/src/{mutex.ts → core/mutex.ts} +0 -0
- /package/src/{worker.ts → core/worker.ts} +0 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useCallback, useState, useTransition } from 'react';
|
|
4
|
+
|
|
5
|
+
//TODO useFetch https://use-http.com
|
|
6
|
+
//TODO SWR?
|
|
7
|
+
//TODO Tanstack Query?
|
|
8
|
+
export function useFetch<R>(
|
|
9
|
+
fn: (...args: any[]) => Promise<R>,
|
|
10
|
+
defaultValue: R | null = null,
|
|
11
|
+
): [R | null, typeof fn, boolean, Error | undefined] {
|
|
12
|
+
// An async function was passed to useActionState, but it was dispatched outside of an action context.
|
|
13
|
+
// This is likely not what you intended. Either pass the dispatch function to an `action` prop, or dispatch manually inside `startTransition`
|
|
14
|
+
const [isPending, startTransition] = useTransition();
|
|
15
|
+
const [data, setData] = useState<R | null>(defaultValue);
|
|
16
|
+
const [error, setError] = useState<Error>();
|
|
17
|
+
|
|
18
|
+
const actionFn = useCallback(
|
|
19
|
+
(...args: Parameters<typeof fn>) => {
|
|
20
|
+
const promise = fn(...args);
|
|
21
|
+
// https://react.dev/reference/react/useTransition#react-doesnt-treat-my-state-update-after-await-as-a-transition
|
|
22
|
+
startTransition(() => promise.then(setData).catch(setError)); //FIXME sub-chain...
|
|
23
|
+
return promise;
|
|
24
|
+
},
|
|
25
|
+
[fn],
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
return [data, actionFn, isPending, error];
|
|
29
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
export function useFetcher<T = any>(
|
|
4
|
+
cb,
|
|
5
|
+
|
|
6
|
+
{ fetchOnMount = false, onError }: { fetchOnMount?: boolean; onError?: (e: Error) => void } = {},
|
|
7
|
+
) {
|
|
8
|
+
const [loading, setLoading] = useState(fetchOnMount);
|
|
9
|
+
const [error, setError] = useState<Error | null>(null);
|
|
10
|
+
const [data, setData] = useState<T | null>(null);
|
|
11
|
+
|
|
12
|
+
const isMounted = useRef(false);
|
|
13
|
+
|
|
14
|
+
const trigger = useCallback(
|
|
15
|
+
async (...args) => {
|
|
16
|
+
try {
|
|
17
|
+
setLoading(true);
|
|
18
|
+
setError(null);
|
|
19
|
+
const res = await cb(args);
|
|
20
|
+
if (!isMounted.current) return;
|
|
21
|
+
setData(res);
|
|
22
|
+
return res;
|
|
23
|
+
} catch (e) {
|
|
24
|
+
console.error('Fetch failed', e);
|
|
25
|
+
if (!isMounted.current) return;
|
|
26
|
+
setError(e);
|
|
27
|
+
(onError as any)?.(e);
|
|
28
|
+
} finally {
|
|
29
|
+
if (isMounted.current) setLoading(false);
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
[cb, onError],
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (fetchOnMount) {
|
|
37
|
+
trigger().catch((e) => console.error('Fetch on mount failed', e)); // catch actually will never happen
|
|
38
|
+
}
|
|
39
|
+
}, [fetchOnMount, cb, trigger]);
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
isMounted.current = true;
|
|
43
|
+
return () => {
|
|
44
|
+
isMounted.current = false;
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
return { loading, error, data, trigger };
|
|
49
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as FileSystem from 'expo-file-system';
|
|
2
|
+
import * as Sharing from 'expo-sharing';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* data:text/plain;base64,W29iamV
|
|
6
|
+
* @see https://stackoverflow.com/questions/69738812/problem-to-generate-pdf-from-a-blob-in-an-expo-app-using-filesystem
|
|
7
|
+
* @param base64
|
|
8
|
+
* @param fileName
|
|
9
|
+
* @returns {Promise<void>}
|
|
10
|
+
*/
|
|
11
|
+
export const shareDialog = async (base64, fileName) => {
|
|
12
|
+
const path = `${FileSystem.documentDirectory}/${fileName}`;
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
if (!(await Sharing.isAvailableAsync())) throw new Error('Sharing is not available');
|
|
16
|
+
|
|
17
|
+
const [header, buffer] = base64.split(',');
|
|
18
|
+
const [, mimeTypeStr] = header.split(':');
|
|
19
|
+
const [mimeType] = mimeTypeStr.split(';');
|
|
20
|
+
|
|
21
|
+
await FileSystem.writeAsStringAsync(`${path}`, buffer, {
|
|
22
|
+
encoding: FileSystem.EncodingType.Base64,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
await Sharing.shareAsync(path, { mimeType, UTI: fileName, dialogTitle: 'Save or share document' });
|
|
26
|
+
} catch (error) {
|
|
27
|
+
alert('Sharing failed: ' + error.message);
|
|
28
|
+
} finally {
|
|
29
|
+
await FileSystem.deleteAsync(path);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as Updates from 'expo-updates';
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { Text } from 'react-native';
|
|
4
|
+
|
|
5
|
+
export function Update({ children }) {
|
|
6
|
+
//FIXME useUpdates https://blog.expo.dev/feature-preview-updates-js-api-for-expo-updates-3b92beb40dab
|
|
7
|
+
const [updating, setUpdating] = useState(true);
|
|
8
|
+
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
(async () => {
|
|
11
|
+
try {
|
|
12
|
+
if (__DEV__) {
|
|
13
|
+
setUpdating(false);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const update = await Updates.checkForUpdateAsync();
|
|
18
|
+
|
|
19
|
+
if (!update.isAvailable) {
|
|
20
|
+
setUpdating(false);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
await Updates.fetchUpdateAsync();
|
|
25
|
+
alert('Update is available. App will reload.');
|
|
26
|
+
await Updates.reloadAsync();
|
|
27
|
+
} catch (error) {
|
|
28
|
+
// You can also add an alert() to see the error message in case of an error when fetching updates.
|
|
29
|
+
alert(`Error fetching latest Expo update: ${error}`);
|
|
30
|
+
setUpdating(false);
|
|
31
|
+
}
|
|
32
|
+
})();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (updating) {
|
|
36
|
+
return <Text>Checking for updates ({Updates.channel})...</Text>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return children;
|
|
40
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { AppState } from 'react-native';
|
|
3
|
+
|
|
4
|
+
export function useAppState() {
|
|
5
|
+
const [appState, setAppState] = useState(AppState.currentState);
|
|
6
|
+
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
const subscription = AppState.addEventListener('change', (nextAppState) => {
|
|
9
|
+
setAppState(nextAppState);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
return () => {
|
|
13
|
+
subscription.remove();
|
|
14
|
+
};
|
|
15
|
+
}, []);
|
|
16
|
+
|
|
17
|
+
return appState;
|
|
18
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export const Fullpage = ({ divRef = null, className = '', children, ...props }) => (
|
|
4
|
+
<div
|
|
5
|
+
ref={divRef}
|
|
6
|
+
{...props}
|
|
7
|
+
className={`container h-screen flex flex-col justify-center items-center ${className}`}
|
|
8
|
+
>
|
|
9
|
+
{children}
|
|
10
|
+
</div>
|
|
11
|
+
);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
|
|
5
|
+
export function ResponsiveHelperTW() {
|
|
6
|
+
return (
|
|
7
|
+
<span className="fixed bottom-0 right-2 z-30 leading-none text-xs">
|
|
8
|
+
<small className="hidden xs:max-sm:block">XS</small>
|
|
9
|
+
<small className="hidden sm:max-md:block">SM</small>
|
|
10
|
+
<small className="hidden md:max-lg:block">MD</small>
|
|
11
|
+
<small className="hidden lg:max-xl:block">LG</small>
|
|
12
|
+
<small className="hidden xl:max-2xl:block">XL</small>
|
|
13
|
+
<small className="hidden 2xl:block">XXL</small>
|
|
14
|
+
</span>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// 'use client';
|
|
2
|
+
//
|
|
3
|
+
// import { Auth0Provider, useAuth0 } from '@auth0/auth0-react';
|
|
4
|
+
// import { Button, ButtonProps } from 'react-bootstrap';
|
|
5
|
+
// import { useRouter } from 'next/navigation';
|
|
6
|
+
// import { useEffect } from 'react';
|
|
7
|
+
// import { emitter } from '../api/apiClient';
|
|
8
|
+
// import RestResponse from '../api/restResponse';
|
|
9
|
+
// import { useToaster } from '../redux/toasterSlice';
|
|
10
|
+
// import { end_session } from '../api/miscCommands';
|
|
11
|
+
// import { createAuth0Client, Auth0Client } from '@auth0/auth0-spa-js';
|
|
12
|
+
//
|
|
13
|
+
// const isSSR = typeof window === 'undefined';
|
|
14
|
+
//
|
|
15
|
+
// const origin = !isSSR ? window.location.origin : '';
|
|
16
|
+
//
|
|
17
|
+
// // @ts-ignore
|
|
18
|
+
// export const isReactNative = () => !isSSR && window.ReactNativeWebView?.postMessage;
|
|
19
|
+
//
|
|
20
|
+
// // @ts-ignore
|
|
21
|
+
// export const postMessage = (data: any = null) => window.ReactNativeWebView?.postMessage(JSON.stringify(data));
|
|
22
|
+
//
|
|
23
|
+
// if (isReactNative()) {
|
|
24
|
+
// const originalConsole = { ...console };
|
|
25
|
+
//
|
|
26
|
+
// const makeLogger =
|
|
27
|
+
// (type) =>
|
|
28
|
+
// (...args) => {
|
|
29
|
+
// postMessage(
|
|
30
|
+
// JSON.stringify({
|
|
31
|
+
// type,
|
|
32
|
+
// data: args,
|
|
33
|
+
// })
|
|
34
|
+
// );
|
|
35
|
+
// originalConsole[type].apply(console, args);
|
|
36
|
+
// };
|
|
37
|
+
//
|
|
38
|
+
// console.log = makeLogger('log');
|
|
39
|
+
// console.log = makeLogger('error');
|
|
40
|
+
// console.log = makeLogger('info');
|
|
41
|
+
// console.log = makeLogger('warn');
|
|
42
|
+
// }
|
|
43
|
+
//
|
|
44
|
+
// export const externalAuth =
|
|
45
|
+
// !isSSR && window['auth0data']
|
|
46
|
+
// ? ({
|
|
47
|
+
// user: window['auth0data'].user,
|
|
48
|
+
// getAccessTokenSilently: async () => window['auth0data'].token.accessToken,
|
|
49
|
+
// isLoading: !window['auth0data'].user,
|
|
50
|
+
// isAuthenticated: !!window['auth0data'].user,
|
|
51
|
+
// loginWithRedirect: () => {},
|
|
52
|
+
// logout: () => postMessage({ type: 'logout' }),
|
|
53
|
+
// } as any)
|
|
54
|
+
// : null;
|
|
55
|
+
//
|
|
56
|
+
// export const useAuth = (): ReturnType<typeof useAuth0> => {
|
|
57
|
+
// const auth0 = useAuth0();
|
|
58
|
+
// return externalAuth || auth0;
|
|
59
|
+
// };
|
|
60
|
+
//
|
|
61
|
+
// // To support passing parameters and hints to Auth0 we need to use lower level API
|
|
62
|
+
// const auth0Config = {
|
|
63
|
+
// domain: process.env.NEXT_PUBLIC_AUTH0_DOMAIN as string,
|
|
64
|
+
// clientId: process.env.NEXT_PUBLIC_AUTH0_CLIENT_ID as string,
|
|
65
|
+
// redirectUri: origin,
|
|
66
|
+
// audience: process.env.NEXT_PUBLIC_AUTH0_AUDIENCE,
|
|
67
|
+
// };
|
|
68
|
+
//
|
|
69
|
+
// let auth0ClientInstance: Auth0Client | null = null;
|
|
70
|
+
//
|
|
71
|
+
// const getAuth0Client = async () => {
|
|
72
|
+
// if (!auth0ClientInstance) {
|
|
73
|
+
// auth0ClientInstance = await createAuth0Client(auth0Config);
|
|
74
|
+
// }
|
|
75
|
+
// return auth0ClientInstance;
|
|
76
|
+
// };
|
|
77
|
+
//
|
|
78
|
+
// export const useExtendedAuth = () => {
|
|
79
|
+
// const auth = useAuth0();
|
|
80
|
+
//
|
|
81
|
+
// const extendedLoginWithRedirect = async (options: any) => {
|
|
82
|
+
// if (options.login_hint || options.screen_hint) {
|
|
83
|
+
// const auth0Client = await getAuth0Client();
|
|
84
|
+
// return auth0Client.loginWithRedirect(options);
|
|
85
|
+
// }
|
|
86
|
+
//
|
|
87
|
+
// return auth.loginWithRedirect(options);
|
|
88
|
+
// };
|
|
89
|
+
//
|
|
90
|
+
// return {
|
|
91
|
+
// ...auth,
|
|
92
|
+
// loginWithRedirect: extendedLoginWithRedirect,
|
|
93
|
+
// };
|
|
94
|
+
// };
|
|
95
|
+
//
|
|
96
|
+
// export const Provider = ({ children, authParams = {} }) => {
|
|
97
|
+
// return (
|
|
98
|
+
// <Auth0Provider
|
|
99
|
+
// domain={process.env.NEXT_PUBLIC_AUTH0_DOMAIN as string}
|
|
100
|
+
// clientId={process.env.NEXT_PUBLIC_AUTH0_CLIENT_ID as string}
|
|
101
|
+
// authorizationParams={{
|
|
102
|
+
// ...authParams,
|
|
103
|
+
// redirect_uri: origin,
|
|
104
|
+
// audience: process.env.NEXT_PUBLIC_AUTH0_AUDIENCE,
|
|
105
|
+
// }}
|
|
106
|
+
// >
|
|
107
|
+
// {children}
|
|
108
|
+
// </Auth0Provider>
|
|
109
|
+
// );
|
|
110
|
+
// };
|
|
111
|
+
//
|
|
112
|
+
// export const LoginButton = (props: ButtonProps) => {
|
|
113
|
+
// const { loginWithRedirect } = useAuth(); //FIXME add state
|
|
114
|
+
// return (
|
|
115
|
+
// <Button {...props} onClick={() => loginWithRedirect()}>
|
|
116
|
+
// Log In
|
|
117
|
+
// </Button>
|
|
118
|
+
// );
|
|
119
|
+
// };
|
|
120
|
+
//
|
|
121
|
+
// export const LogoutButton = ({ as: Cmp = Button, children, ...props }: any) => {
|
|
122
|
+
// const { logout, isAuthenticated, isLoading } = useAuth();
|
|
123
|
+
// if (!isAuthenticated || isLoading) return null;
|
|
124
|
+
//
|
|
125
|
+
// const handleLogout = async () => {
|
|
126
|
+
// try {
|
|
127
|
+
// await end_session();
|
|
128
|
+
// } catch (e) {
|
|
129
|
+
// console.error('Cannot end session', e); // will get here if in Native
|
|
130
|
+
// }
|
|
131
|
+
// logout({ logoutParams: { returnTo: origin } });
|
|
132
|
+
// };
|
|
133
|
+
//
|
|
134
|
+
// return (
|
|
135
|
+
// <Cmp {...props} onClick={handleLogout} role="button">
|
|
136
|
+
// {children || 'Log Out'}
|
|
137
|
+
// </Cmp>
|
|
138
|
+
// );
|
|
139
|
+
// };
|
|
140
|
+
//
|
|
141
|
+
// export const useLogoutTracker = () => {
|
|
142
|
+
// const router = useRouter();
|
|
143
|
+
// const { logout } = useAuth();
|
|
144
|
+
// const { setToast } = useToaster();
|
|
145
|
+
//
|
|
146
|
+
// useEffect(() => {
|
|
147
|
+
// let onError;
|
|
148
|
+
//
|
|
149
|
+
// emitter.on(
|
|
150
|
+
// 'error',
|
|
151
|
+
// (onError = (restResponse: RestResponse) => {
|
|
152
|
+
// if (restResponse.code === 401 || restResponse.message.includes('(Unauthorized)')) {
|
|
153
|
+
// if (restResponse.message.includes('OpenAI API Key')) {
|
|
154
|
+
// setToast('Your account is not provisioned yet. Please check your email for instructions.');
|
|
155
|
+
// return;
|
|
156
|
+
// }
|
|
157
|
+
//
|
|
158
|
+
// if (externalAuth) {
|
|
159
|
+
// externalAuth.logout();
|
|
160
|
+
// return;
|
|
161
|
+
// }
|
|
162
|
+
//
|
|
163
|
+
// logout({
|
|
164
|
+
// async openUrl() {
|
|
165
|
+
// router.replace('/' as any); //FIXME Return to the same page
|
|
166
|
+
// setToast('You have been logged out');
|
|
167
|
+
// },
|
|
168
|
+
// });
|
|
169
|
+
// }
|
|
170
|
+
// })
|
|
171
|
+
// );
|
|
172
|
+
//
|
|
173
|
+
// return () => {
|
|
174
|
+
// emitter.off('error', onError);
|
|
175
|
+
// };
|
|
176
|
+
// }, [logout, router, setToast]);
|
|
177
|
+
// };
|
package/tsconfig.json
CHANGED
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
{
|
|
2
|
+
"extends": "../../tsconfig.json",
|
|
2
3
|
"compilerOptions": {
|
|
4
|
+
"rootDir": "src",
|
|
5
|
+
"outDir": "dist",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"declarationMap": true,
|
|
8
|
+
"declarationDir": "dist",
|
|
9
|
+
"incremental": false,
|
|
3
10
|
"allowJs": true,
|
|
4
|
-
|
|
5
|
-
"
|
|
6
|
-
"
|
|
11
|
+
/* Build-specific options */
|
|
12
|
+
"moduleResolution": "bundler",
|
|
13
|
+
"allowImportingTsExtensions": true,
|
|
7
14
|
"isolatedModules": true,
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"module": "esnext",
|
|
12
|
-
"moduleResolution": "node",
|
|
13
|
-
"resolveJsonModule": true,
|
|
14
|
-
"skipLibCheck": true,
|
|
15
|
-
"strict": false,
|
|
16
|
-
"strictNullChecks": true,
|
|
17
|
-
"incremental": true,
|
|
18
|
-
"outDir": "./build",
|
|
19
|
-
"declaration": true,
|
|
20
|
-
"declarationDir": "./build",
|
|
21
|
-
"rootDir": "src"
|
|
15
|
+
"moduleDetection": "force",
|
|
16
|
+
"noEmit": true,
|
|
17
|
+
"jsx": "react-jsx"
|
|
22
18
|
},
|
|
23
|
-
"include": ["src"]
|
|
19
|
+
"include": ["src"],
|
|
20
|
+
"exclude": ["**/*.test.ts", "**/*.stories.ts", "**/*.stories.tsx"]
|
|
24
21
|
}
|
package/turbo.json
CHANGED
|
@@ -2,18 +2,9 @@
|
|
|
2
2
|
"$schema": "https://turbo.build/schema.json",
|
|
3
3
|
"extends": ["//"],
|
|
4
4
|
"tasks": {
|
|
5
|
-
"clean": {
|
|
6
|
-
"cache": false
|
|
7
|
-
},
|
|
8
5
|
"build": {
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
"start": {
|
|
12
|
-
"persistent": true,
|
|
13
|
-
"cache": false
|
|
14
|
-
},
|
|
15
|
-
"wait": {
|
|
16
|
-
"cache": false
|
|
6
|
+
"dependsOn": ["^build"],
|
|
7
|
+
"outputs": ["src/**/*/index.ts", "package.json", "dist/**/*"]
|
|
17
8
|
}
|
|
18
9
|
}
|
|
19
10
|
}
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { defineConfig } from 'vitest/config';
|
|
2
|
+
import react from '@vitejs/plugin-react';
|
|
3
|
+
import dts from 'vite-plugin-dts';
|
|
4
|
+
import { fixExports, formats, entry, external, distDir, generateIndex } from './vite.exports';
|
|
5
|
+
|
|
6
|
+
// https://rbardini.com/how-to-build-ts-library-with-vite/
|
|
7
|
+
// https://dev.to/receter/how-to-create-a-react-component-library-using-vites-library-mode-4lma
|
|
8
|
+
|
|
9
|
+
export default defineConfig({
|
|
10
|
+
build: {
|
|
11
|
+
ssr: true,
|
|
12
|
+
sourcemap: true,
|
|
13
|
+
outDir: distDir,
|
|
14
|
+
emptyOutDir: true,
|
|
15
|
+
// target: 'esnext',
|
|
16
|
+
lib: {
|
|
17
|
+
entry,
|
|
18
|
+
formats: Object.keys(formats) as any,
|
|
19
|
+
},
|
|
20
|
+
rollupOptions: {
|
|
21
|
+
external,
|
|
22
|
+
output: {
|
|
23
|
+
preserveModules: true,
|
|
24
|
+
preserveModulesRoot: 'src',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
test: {
|
|
29
|
+
coverage: {
|
|
30
|
+
reporter: ['text', 'html', 'cobertura'],
|
|
31
|
+
} as any,
|
|
32
|
+
},
|
|
33
|
+
resolve: { extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'] }, // https://vite.dev/guide/performance#reduce-resolve-operations
|
|
34
|
+
plugins: [
|
|
35
|
+
react(),
|
|
36
|
+
dts(), //TODO Check https://github.com/alloc/vite-dts
|
|
37
|
+
{
|
|
38
|
+
name: 'Generate Index & Exports',
|
|
39
|
+
async buildStart() {
|
|
40
|
+
await generateIndex();
|
|
41
|
+
},
|
|
42
|
+
async closeBundle() {
|
|
43
|
+
await fixExports();
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
});
|
package/vite.exports.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import * as process from 'node:process';
|
|
4
|
+
import { globSync } from 'glob';
|
|
5
|
+
import { execSync } from 'node:child_process';
|
|
6
|
+
|
|
7
|
+
export const pkgPath = path.join(process.cwd(), 'package.json');
|
|
8
|
+
|
|
9
|
+
export const distDir = './dist';
|
|
10
|
+
|
|
11
|
+
//TODO Re-read in fixExports
|
|
12
|
+
// import pkg from './package.json' assert { type: 'json' };
|
|
13
|
+
// export { pkg };
|
|
14
|
+
export const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
15
|
+
|
|
16
|
+
export const rootPkg = JSON.parse(fs.readFileSync(path.resolve('../../package.json'), 'utf-8'));
|
|
17
|
+
|
|
18
|
+
export const formats = {
|
|
19
|
+
// cjs: ['require', 'js', 'd.ts'],
|
|
20
|
+
esm: ['import', 'mjs', 'd.ts'],
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const entryGlob = 'src/*/index.ts';
|
|
24
|
+
export const excludeGlob = 'src/**/*.{stories,test}.{ts,tsx}';
|
|
25
|
+
export const includeGlob = 'src/**/!(*.stories|*.test).{ts,tsx}';
|
|
26
|
+
export const foldersGlob = 'src/*/';
|
|
27
|
+
|
|
28
|
+
export const external = [
|
|
29
|
+
...[...Object.keys(pkg.dependencies), ...Object.keys(pkg.devDependencies)].map(
|
|
30
|
+
(dep) => new RegExp(`^${dep}(/.*)?`),
|
|
31
|
+
),
|
|
32
|
+
/node_modules/,
|
|
33
|
+
/^node:.*/,
|
|
34
|
+
/builder-util-runtime/,
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
export const entry = globSync(entryGlob);
|
|
38
|
+
|
|
39
|
+
export async function fixExports() {
|
|
40
|
+
const modules = globSync(foldersGlob).map((file) => path.basename(file));
|
|
41
|
+
|
|
42
|
+
// Exports
|
|
43
|
+
|
|
44
|
+
pkg.exports = {};
|
|
45
|
+
|
|
46
|
+
// https://johnnyreilly.com/dual-publishing-esm-cjs-modules-with-tsup-and-are-the-types-wrong
|
|
47
|
+
// https://www.npmjs.com/package/@arethetypeswrong/cli
|
|
48
|
+
// FIXME https://github.com/andrewbranch/example-subpath-exports-ts-compat
|
|
49
|
+
for (const entry of modules) {
|
|
50
|
+
const key = `./${entry}`;
|
|
51
|
+
|
|
52
|
+
pkg.exports[key] = {};
|
|
53
|
+
|
|
54
|
+
for (const [format, ext, type] of Object.values(formats)) {
|
|
55
|
+
const fileName = `${distDir}/${entry}/index`;
|
|
56
|
+
|
|
57
|
+
pkg.exports[key][format] = {
|
|
58
|
+
[format]: `${fileName}.${ext}`,
|
|
59
|
+
types: `${fileName}.${type}`,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const core = pkg.exports['./core'];
|
|
65
|
+
|
|
66
|
+
pkg.exports['.'] = core;
|
|
67
|
+
|
|
68
|
+
// Main
|
|
69
|
+
|
|
70
|
+
pkg.type = 'module';
|
|
71
|
+
pkg.main = pkg.module = core.import.import;
|
|
72
|
+
pkg.types = core.import.types;
|
|
73
|
+
|
|
74
|
+
// Author & License
|
|
75
|
+
|
|
76
|
+
// TODO Move to root build + set private=true for demos + set react version
|
|
77
|
+
pkg.publishConfig = { access: 'public' };
|
|
78
|
+
pkg.author = 'Kirill Konshin <kirill@konshin.org> (https://konshin.org)';
|
|
79
|
+
pkg.license = 'MIT';
|
|
80
|
+
|
|
81
|
+
// Scripts
|
|
82
|
+
|
|
83
|
+
// pkg.scripts.clean = 'rm -rf dist .tscache tsconfig.tsbuildinfo';
|
|
84
|
+
// pkg.scripts.build = 'vite build';
|
|
85
|
+
// pkg.scripts.start = 'yarn build --watch';
|
|
86
|
+
pkg.scripts.wait = `wait-on ${core.import.import}`;
|
|
87
|
+
|
|
88
|
+
// Ensure peer deps are correct & meta is set
|
|
89
|
+
|
|
90
|
+
pkg.peerDependenciesMeta = {};
|
|
91
|
+
|
|
92
|
+
for (const [key, _] of Object.entries(pkg.peerDependencies)) {
|
|
93
|
+
const root = rootPkg.devDependencies[key]; // lifted to root
|
|
94
|
+
const srcPkg = root ? rootPkg : pkg;
|
|
95
|
+
|
|
96
|
+
if (!srcPkg.devDependencies[key]) throw new Error(`Key ${key} not found in dependencies`);
|
|
97
|
+
|
|
98
|
+
pkg.peerDependenciesMeta[key] = { optional: true };
|
|
99
|
+
|
|
100
|
+
const version = srcPkg.devDependencies[key].split('.');
|
|
101
|
+
|
|
102
|
+
pkg.peerDependencies[key] = version[0] === '^0' ? `${version[0]}.${version[1]}` : version[0];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Write
|
|
106
|
+
|
|
107
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
|
|
108
|
+
|
|
109
|
+
console.log('Updated package.json with exports');
|
|
110
|
+
// console.log(pkg);
|
|
111
|
+
// console.log(pkg.exports);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export async function generateIndex() {
|
|
115
|
+
execSync(`yarn build:index`, { stdio: 'inherit' }); // programmatic call
|
|
116
|
+
//FIXME https://github.com/imjuni/create-ts-index/issues/70
|
|
117
|
+
// import { TypeScritIndexWriter } from 'create-ts-index';
|
|
118
|
+
// const tsiw = new TypeScritIndexWriter();
|
|
119
|
+
// const options = TypeScritIndexWriter.getDefaultOption('./src');
|
|
120
|
+
// await tsiw.createEntrypoint(option);
|
|
121
|
+
}
|