init
This commit is contained in:
19
.cnb.yml
Normal file
19
.cnb.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
# .cnb.yml
|
||||
include:
|
||||
- https://cnb.cool/kevisual/cnb/-/blob/main/.cnb/template.yml
|
||||
|
||||
.common_env: &common_env
|
||||
env:
|
||||
USERNAME: root
|
||||
imports:
|
||||
- https://cnb.cool/kevisual/env/-/blob/main/.env.development
|
||||
|
||||
$:
|
||||
vscode:
|
||||
- docker:
|
||||
image: docker.cnb.cool/kevisual/dev-env:latest
|
||||
services:
|
||||
- vscode
|
||||
- docker
|
||||
imports: !reference [.common_env, imports]
|
||||
stages: !reference [.dev_template, stages]
|
||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
storage
|
||||
node_modules
|
||||
2
.npmrc
Normal file
2
.npmrc
Normal file
@@ -0,0 +1,2 @@
|
||||
//npm.xiongxiao.me/:_authToken=${ME_NPM_TOKEN}
|
||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
||||
25
bun.config.mjs
Normal file
25
bun.config.mjs
Normal file
@@ -0,0 +1,25 @@
|
||||
// @ts-check
|
||||
import { resolvePath } from '@kevisual/use-config';
|
||||
import { execSync } from 'node:child_process';
|
||||
|
||||
const entry = 'src/index.ts';
|
||||
const naming = 'app';
|
||||
const external = ['bullmq','ioredis'];
|
||||
/**
|
||||
* @type {import('bun').BuildConfig}
|
||||
*/
|
||||
await Bun.build({
|
||||
target: 'node',
|
||||
format: 'esm',
|
||||
entrypoints: [resolvePath(entry, { meta: import.meta })],
|
||||
outdir: resolvePath('./dist', { meta: import.meta }),
|
||||
naming: {
|
||||
entry: `${naming}.js`,
|
||||
},
|
||||
external: external,
|
||||
env: 'KEVISUAL_*',
|
||||
});
|
||||
|
||||
// const cmd = `dts -i src/index.ts -o app.d.ts`;
|
||||
const cmd = `dts -i ${entry} -o ${naming}.d.ts`;
|
||||
execSync(cmd, { stdio: 'inherit' });
|
||||
35
package.json
Normal file
35
package.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "@kevisual/context",
|
||||
"version": "0.0.4",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev": "bun src/dev.ts --watch",
|
||||
"build": "bun bun.config.mjs",
|
||||
"publish": "ev npm publish npm -p"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"src"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
|
||||
"license": "MIT",
|
||||
"packageManager": "pnpm@10.14.0",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/app.js",
|
||||
"types": "./dist/app.d.ts"
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kevisual/load": "^0.0.6",
|
||||
"@kevisual/types": "^0.0.10",
|
||||
"@kevisual/use-config": "^1.0.19",
|
||||
"@types/node": "^24.2.1"
|
||||
}
|
||||
}
|
||||
40
readme.md
Normal file
40
readme.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# 环境变量 context
|
||||
|
||||
上下文 的node或则浏览器当中,如果global中不存在context,则赋值到global,如果存在,则获取对应的。
|
||||
|
||||
```ts
|
||||
import { useContextKey } from '@kevisual/context';
|
||||
|
||||
const v = useContextKey('a', () => '123'); // 如果是第一次初始化,返回123,否则返回之前初始化的值
|
||||
```
|
||||
|
||||
|
||||
|
||||
### types
|
||||
```ts
|
||||
type GlobalContext<T = SimpleObject> = {
|
||||
name?: string;
|
||||
[key: string]: any;
|
||||
} & T;
|
||||
declare const useEnv: <T = SimpleObject>(initEnv?: GlobalContext<T>, initKey?: string, isOverwrite?: boolean) => Required<GlobalContext<T>>;
|
||||
type InitResult<T> = T | Promise<T> | null;
|
||||
type InitFn<T> = () => T | Promise<T>;
|
||||
type Init<T> = T | InitFn<T> | null;
|
||||
type AsyncResult<T, ASYNC extends boolean = false> = ASYNC extends true ? Promise<T> : T;
|
||||
type SimpleObject = Record<string, any>;
|
||||
declare const useEnvKey: <T = any>(key: string, init?: Init<T>, initKey?: string) => InitResult<T>;
|
||||
declare const usePageEnv: (init?: Init<any>, initKey?: string) => any;
|
||||
declare const useEnvKeyNew: (key: string, initKey?: string, opts?: {
|
||||
getNew?: boolean;
|
||||
init?: Init<any>;
|
||||
}) => any;
|
||||
declare const useContext: <T = SimpleObject>(initContext?: GlobalContext<T>, isOverwrite?: boolean) => Required<GlobalContext<T>>;
|
||||
declare const useContextKey: <T = any, ASYNC extends boolean = false>(key: string, init?: Init<T>, isNew?: boolean) => AsyncResult<T, ASYNC>;
|
||||
declare const usePageContext: (init?: Init<any>) => any;
|
||||
declare const useConfig: <T = SimpleObject>(initConfig?: Partial<GlobalContext<T>>, isOverwrite?: boolean) => Required<GlobalContext<Partial<GlobalContext<T>>>>;
|
||||
declare const useConfigKey: <T = any, ASYNC extends boolean = false>(key: string, init?: Init<T>, isNew?: boolean) => AsyncResult<T, ASYNC>;
|
||||
declare const usePageConfig: (init?: Init<any>) => any;
|
||||
|
||||
export { useConfig, useConfigKey, useContext, useContextKey, useEnv, useEnvKey, useEnvKeyNew, usePageConfig, usePageContext, usePageEnv };
|
||||
|
||||
```
|
||||
201
src/index.ts
Normal file
201
src/index.ts
Normal file
@@ -0,0 +1,201 @@
|
||||
import { isBrowser } from './utils/browser.ts';
|
||||
import { getPathKey } from './utils/path-key.ts';
|
||||
import { BaseLoad } from '@kevisual/load';
|
||||
|
||||
const gt = (globalThis as any) || window || self;
|
||||
|
||||
type GlobalContext<T = SimpleObject> = {
|
||||
name?: string;
|
||||
[key: string]: any;
|
||||
} & T;
|
||||
// 从window对象中获取全局的环境变量,如果没有则初始化一个
|
||||
export const useEnv = <T = SimpleObject>(initEnv?: GlobalContext<T>, initKey = 'config', isOverwrite?: boolean) => {
|
||||
const env: GlobalContext<T> = gt[initKey];
|
||||
const _env = env || initEnv;
|
||||
// 环境不存在则初始化一个
|
||||
if (!env) {
|
||||
if (_env) {
|
||||
gt[initKey] = _env;
|
||||
} else {
|
||||
gt[initKey] = {};
|
||||
}
|
||||
} else if (isOverwrite) {
|
||||
// 环境存在且是覆盖模式
|
||||
gt[initKey] = { ...env, ...initEnv };
|
||||
}
|
||||
|
||||
return gt[initKey] as Required<GlobalContext<T>>;
|
||||
};
|
||||
type InitResult<T> = T | Promise<T> | null;
|
||||
type InitFn<T> = () => T | Promise<T>;
|
||||
type Init<T> = T | InitFn<T> | null;
|
||||
type AsyncResult<T, ASYNC extends boolean = false> = ASYNC extends true ? Promise<T> : T;
|
||||
type SimpleObject = Record<string, any>;
|
||||
|
||||
// 从全局环境变量中获取指定的key值,如果没有则初始化一个, key不存在,返回Env对象
|
||||
export const useEnvKey = <T = any>(key: string, init?: Init<T>, initKey = 'config'): InitResult<T> => {
|
||||
const _env = useEnv({}, initKey);
|
||||
// 已经存在,直接返回
|
||||
if (key && typeof _env[key] !== 'undefined') {
|
||||
return _env[key];
|
||||
}
|
||||
// 不存在,但是有初始化函数,初始化的返回,同步函数,删除了重新加载?
|
||||
if (key && init) {
|
||||
if (typeof init !== 'function') {
|
||||
_env[key] = init as T;
|
||||
}
|
||||
// 如果是函数,执行函数
|
||||
if (typeof init === 'function') {
|
||||
const result = (init as InitFn<T>)();
|
||||
// 如果结果是Promise,处理异步结果
|
||||
if (result instanceof Promise) {
|
||||
return result.then((res) => {
|
||||
_env[key] = res;
|
||||
return res as T;
|
||||
});
|
||||
}
|
||||
_env[key] = result;
|
||||
}
|
||||
return _env[key];
|
||||
}
|
||||
if (key) {
|
||||
// 加载
|
||||
const baseLoad = new BaseLoad();
|
||||
const voidFn = async () => {
|
||||
return _env[key];
|
||||
};
|
||||
const checkFn = async () => {
|
||||
const loadRes = await baseLoad.load(voidFn, {
|
||||
key,
|
||||
isReRun: true,
|
||||
checkSuccess: () => _env[key],
|
||||
timeout: 5 * 60 * 1000,
|
||||
interval: 1000,
|
||||
//
|
||||
});
|
||||
if (loadRes.code !== 200) {
|
||||
console.error('load key error');
|
||||
return null;
|
||||
}
|
||||
return _env[key];
|
||||
};
|
||||
return checkFn();
|
||||
}
|
||||
// 不存在,没有初始化函数
|
||||
console.error('key is empty ');
|
||||
return null;
|
||||
};
|
||||
|
||||
export const usePageEnv = (init?: Init<any>, initKey = 'config') => {
|
||||
const { id } = getPathKey();
|
||||
return useEnvKey(id, init, initKey);
|
||||
};
|
||||
export const useEnvKeyNew = (key: string, initKey = 'config', opts?: { getNew?: boolean; init?: Init<any> }) => {
|
||||
const _env = useEnv({}, initKey);
|
||||
if (key) {
|
||||
delete _env[key];
|
||||
}
|
||||
if (opts?.getNew && opts.init) {
|
||||
return useEnvKey(key, opts.init, initKey);
|
||||
} else if (opts?.getNew) {
|
||||
return useEnvKey(key, null, initKey);
|
||||
}
|
||||
};
|
||||
|
||||
export const useContext = <T = SimpleObject>(initContext?: GlobalContext<T>, isOverwrite?: boolean) => {
|
||||
return useEnv(initContext, 'context', isOverwrite);
|
||||
};
|
||||
|
||||
export const useContextKey = <T = any, ASYNC extends boolean = false>(key: string, init?: Init<T>, isNew?: boolean): AsyncResult<T, ASYNC> => {
|
||||
if (isNew) {
|
||||
return useEnvKeyNew(key, 'context', { getNew: true, init }) as any;
|
||||
}
|
||||
return useEnvKey(key, init, 'context') as any;
|
||||
};
|
||||
|
||||
export const use = useContextKey;
|
||||
|
||||
export const usePageContext = (init?: Init<any>) => {
|
||||
const { id } = getPathKey();
|
||||
return useContextKey(id, init);
|
||||
};
|
||||
|
||||
export const useConfig = <T = SimpleObject>(initConfig?: Partial<GlobalContext<T>>, isOverwrite?: boolean) => {
|
||||
return useEnv(initConfig, 'config', isOverwrite);
|
||||
};
|
||||
|
||||
export const useConfigKey = <T = any, ASYNC extends boolean = false>(key: string, init?: Init<T>, isNew?: boolean): AsyncResult<T, ASYNC> => {
|
||||
if (isNew) {
|
||||
return useEnvKeyNew(key, 'config', { getNew: true, init });
|
||||
}
|
||||
return useEnvKey(key, init, 'config') as any;
|
||||
};
|
||||
|
||||
export const usePageConfig = (init?: Init<any>) => {
|
||||
const { id } = getPathKey();
|
||||
return useConfigKey(id, init);
|
||||
};
|
||||
|
||||
class InitEnv {
|
||||
static isInit = false;
|
||||
|
||||
static init(opts?: { load?: boolean; page?: boolean }) {
|
||||
if (InitEnv.isInit) {
|
||||
return;
|
||||
}
|
||||
const { load = true, page = false } = opts || {};
|
||||
InitEnv.isInit = true;
|
||||
// bind to window, 必须要的获取全局的环境变量
|
||||
// @ts-ignore
|
||||
gt.useConfigKey = useConfigKey;
|
||||
// @ts-ignore
|
||||
gt.useContextKey = useContextKey;
|
||||
// @ts-ignore
|
||||
gt.use = use;
|
||||
// @ts-ignore
|
||||
gt.webEnv = { useConfigKey, useContextKey, use };
|
||||
// @ts-ignore
|
||||
load && (gt.Load = BaseLoad);
|
||||
}
|
||||
}
|
||||
InitEnv.init();
|
||||
|
||||
|
||||
/**
|
||||
* 从环境变量获取
|
||||
* @param envKey
|
||||
* @param initKey
|
||||
* @returns
|
||||
*/
|
||||
export const useKey = <T = string>(envKey: T, initKey = 'context') => {
|
||||
let key: string = envKey as unknown as string;
|
||||
if (!key) return null;
|
||||
const _env = useEnv({}, initKey);
|
||||
// 已经存在,直接返回
|
||||
if (typeof _env[key] !== 'undefined') {
|
||||
return _env[key];
|
||||
}
|
||||
if (!isBrowser) {
|
||||
// 如果是node类,从process.env获取对应的环境变量
|
||||
const nodeEev = gt?.process?.env;
|
||||
if (typeof nodeEev !== 'undefined') {
|
||||
const value = nodeEev[key];
|
||||
if (typeof value !== 'undefined') {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* 从浏览器的storage获取变量值
|
||||
*/
|
||||
const storage = gt?.localStorage;
|
||||
if (typeof storage !== 'undefined') {
|
||||
const value = storage.getItem(key);
|
||||
if (typeof value !== 'undefined') {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
33
src/test/a.ts
Normal file
33
src/test/a.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { useConfigKey, useConfig } from '@/index.ts';
|
||||
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
const v = useConfigKey<{ name: string }>('a', async () => {
|
||||
await sleep(1000);
|
||||
return {
|
||||
name: 'a',
|
||||
};
|
||||
});
|
||||
|
||||
console.log('v', v.name);
|
||||
|
||||
const v2 = await useConfigKey<{ name: string }, true>('a');
|
||||
|
||||
console.log('v2', v2.name);
|
||||
|
||||
const v3 = useConfigKey<{ name: string }>('a', {
|
||||
name: 'bbba',
|
||||
});
|
||||
|
||||
console.log('v3', v3.name);
|
||||
|
||||
const b = useConfig<{ f: string }>(
|
||||
{
|
||||
name: 'b',
|
||||
age: 18,
|
||||
getName: () => {
|
||||
return 'b';
|
||||
},
|
||||
},
|
||||
true,
|
||||
);
|
||||
|
||||
console.log('b', b.name, b.age, b.getName?.());
|
||||
1
src/utils/browser.ts
Normal file
1
src/utils/browser.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
|
||||
14
src/utils/path-key.ts
Normal file
14
src/utils/path-key.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export const getPathKey = () => {
|
||||
if (!isBrowser) {
|
||||
return { path: '', key: '', id: '', prefix: '' };
|
||||
}
|
||||
// 从localtion.href的路径中,/a/b 中 a为path,b为key
|
||||
const pathname = location.pathname;
|
||||
const paths = pathname.split('/');
|
||||
let [path, key] = paths.slice(1);
|
||||
path = path || '';
|
||||
key = key || '';
|
||||
return { path, key, id: path + '---' + key, prefix: `/${path}/${key}` };
|
||||
};
|
||||
|
||||
const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
|
||||
20
tsconfig.json
Normal file
20
tsconfig.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"extends": "@kevisual/types/json/backend.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"typeRoots": [
|
||||
"node_modules/@types",
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
],
|
||||
"*": [
|
||||
"types/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
}
|
||||
Reference in New Issue
Block a user