Files
smartenv/ts/smartenv.classes.smartenv.ts

260 lines
7.2 KiB
TypeScript

import * as plugins from './smartenv.plugins.js';
import * as interfaces from './interfaces/index.js';
// interfaces
export interface IEnvObject {
name: string;
value: string;
}
/**
* Smartenv class that makes it easy
*/
export class Smartenv {
public async getEnvAwareModule(optionsArg: {
nodeModuleName: string;
webUrlArg: string;
getFunction: () => any;
}) {
if (this.isNode) {
const moduleResult = await this.getSafeNodeModule(optionsArg.nodeModuleName);
return moduleResult;
} else if (this.isBrowser) {
const moduleResult = await this.getSafeWebModule(
optionsArg.webUrlArg,
optionsArg.getFunction
);
return moduleResult;
} else {
console.error('platform for loading not supported by smartenv');
}
}
public async getSafeNodeModule<T = any>(moduleNameArg: string, runAfterFunc?: (moduleArg: T) => Promise<any>): Promise<T> {
if (!this.isNode && !this.isDeno && !this.isBun) {
console.error(`You tried to load a server module in a wrong context: ${moduleNameArg}. This does not throw.`);
return;
}
// tslint:disable-next-line: function-constructor
const returnValue: T = await (new Function(`return import('${moduleNameArg}')`)() as Promise<T>);
if (runAfterFunc) {
await runAfterFunc(returnValue);
}
return returnValue;
}
public loadedScripts: string[] = [];
public async getSafeWebModule(urlArg: string, getFunctionArg: () => any) {
if (!this.isBrowser) {
console.error('You tried to load a web module in a wrong context');
return;
}
if (this.loadedScripts.includes(urlArg)) {
return getFunctionArg();
} else {
this.loadedScripts.push(urlArg);
}
const done = plugins.smartpromise.defer();
if (globalThis.importScripts) {
globalThis.importScripts(urlArg);
done.resolve();
} else {
const script = document.createElement('script');
script.onload = () => {
done.resolve();
};
script.src = urlArg;
document.head.appendChild(script);
}
await done.promise;
return getFunctionArg();
}
public get runtimeEnv(): interfaces.TRuntimeType {
// Check Deno first (most distinctive)
if (typeof globalThis.Deno !== 'undefined' &&
typeof (globalThis as any).Deno?.version !== 'undefined') {
return 'deno';
}
// Check Bun second (most distinctive)
if (typeof globalThis.Bun !== 'undefined' &&
typeof (globalThis as any).Bun?.version !== 'undefined') {
return 'bun';
}
// Check Node.js (be explicit about versions to avoid Deno/Bun false positives)
if (typeof globalThis.process !== 'undefined' &&
typeof (globalThis as any).process?.versions?.node !== 'undefined') {
return 'node';
}
// Check Browser (default fallback)
if (typeof globalThis.window !== 'undefined' &&
typeof (globalThis as any).document !== 'undefined') {
return 'browser';
}
// Safe fallback
return 'browser';
}
public get isBrowser(): boolean {
return this.runtimeEnv === 'browser';
}
public get isNode(): boolean {
return this.runtimeEnv === 'node';
}
public get isDeno(): boolean {
return this.runtimeEnv === 'deno';
}
public get isBun(): boolean {
return this.runtimeEnv === 'bun';
}
public get userAgent(): string {
if (this.isBrowser) {
// make sure we are in Browser
return navigator.userAgent;
} else {
return 'undefined';
}
}
public get nodeVersion(): string {
if (this.isNode) {
return process.version;
}
return 'undefined';
}
public get denoVersion(): string {
if (this.isDeno) {
return (globalThis as any).Deno.version.deno;
}
return 'undefined';
}
public get bunVersion(): string {
if (this.isBun) {
return (globalThis as any).Bun.version;
}
return 'undefined';
}
/**
* Load a module only if the current runtime matches the target runtime(s)
* @param target - Single runtime, array of runtimes, or 'server' for all server-side runtimes
* @param moduleNameOrUrl - Module name (for Node/Deno/Bun) or URL (for browser)
* @param getFunction - Optional function to retrieve the module in browser context
* @returns The loaded module or undefined if runtime doesn't match
*/
public async getSafeModuleFor<T = any>(
target: interfaces.TRuntimeTarget | interfaces.TRuntimeTarget[],
moduleNameOrUrl: string,
getFunction?: () => any
): Promise<T | undefined> {
// Normalize target to array
let targetRuntimes: interfaces.TRuntimeType[];
if (Array.isArray(target)) {
// Expand 'server' if present in array
targetRuntimes = target.flatMap(t =>
t === 'server' ? ['node', 'deno', 'bun'] as interfaces.TRuntimeType[] : [t as interfaces.TRuntimeType]
);
} else if (target === 'server') {
targetRuntimes = ['node', 'deno', 'bun'];
} else {
targetRuntimes = [target];
}
// Check if current runtime matches any target
if (!targetRuntimes.includes(this.runtimeEnv)) {
console.warn(
`Module "${moduleNameOrUrl}" requested for runtime(s) [${targetRuntimes.join(', ')}] ` +
`but current runtime is "${this.runtimeEnv}". Skipping load.`
);
return undefined;
}
// Load based on current runtime
if (this.isNode || this.isDeno || this.isBun) {
// Server-side runtimes use dynamic import
const moduleResult = await this.getSafeNodeModule<T>(moduleNameOrUrl);
return moduleResult;
} else if (this.isBrowser) {
if (!getFunction) {
console.error(`Browser module load requires getFunction parameter for "${moduleNameOrUrl}"`);
return undefined;
}
const moduleResult = await this.getSafeWebModule(moduleNameOrUrl, getFunction);
return moduleResult as T;
}
return undefined;
}
public get isCI(): boolean {
if (this.isNode) {
if (process.env.CI) {
return true;
} else {
return false;
}
} else {
return false;
}
}
public async isMacAsync(): Promise<boolean> {
if (this.isNode) {
const os = await this.getSafeNodeModule('os');
return os.platform() === 'darwin';
} else {
return false;
}
}
public async isWindowsAsync(): Promise<boolean> {
if (this.isNode) {
const os = await this.getSafeNodeModule('os');
return os.platform() === 'win32';
} else {
return false;
}
}
public async isLinuxAsync(): Promise<boolean> {
if (this.isNode) {
const os = await this.getSafeNodeModule('os');
return os.platform() === 'linux';
} else {
return false;
}
}
/**
* prints the environment to console
*/
public async printEnv() {
if (this.isNode) {
console.log('running on NODE');
console.log('node version is ' + this.nodeVersion);
} else if (this.isDeno) {
console.log('running on DENO');
console.log('deno version is ' + this.denoVersion);
} else if (this.isBun) {
console.log('running on BUN');
console.log('bun version is ' + this.bunVersion);
} else {
console.log('running on BROWSER');
console.log('browser is ' + this.userAgent);
}
}
}