2022-03-16 16:09:24 +01:00
|
|
|
import * as plugins from './smartenv.plugins.js';
|
|
|
|
|
import * as interfaces from './interfaces/index.js';
|
2017-05-17 15:59:10 +02:00
|
|
|
|
|
|
|
|
// interfaces
|
|
|
|
|
export interface IEnvObject {
|
2019-06-17 08:46:28 +02:00
|
|
|
name: string;
|
|
|
|
|
value: string;
|
2017-05-17 15:59:10 +02:00
|
|
|
}
|
|
|
|
|
|
2019-06-17 08:46:28 +02:00
|
|
|
/**
|
|
|
|
|
* Smartenv class that makes it easy
|
|
|
|
|
*/
|
2017-05-17 15:59:10 +02:00
|
|
|
export class Smartenv {
|
2020-09-29 18:38:21 +00:00
|
|
|
public async getEnvAwareModule(optionsArg: {
|
|
|
|
|
nodeModuleName: string;
|
|
|
|
|
webUrlArg: string;
|
|
|
|
|
getFunction: () => any;
|
|
|
|
|
}) {
|
|
|
|
|
if (this.isNode) {
|
2022-12-28 19:43:48 +01:00
|
|
|
const moduleResult = await this.getSafeNodeModule(optionsArg.nodeModuleName);
|
2020-09-29 18:38:21 +00:00
|
|
|
return moduleResult;
|
|
|
|
|
} else if (this.isBrowser) {
|
2022-12-28 19:43:48 +01:00
|
|
|
const moduleResult = await this.getSafeWebModule(
|
|
|
|
|
optionsArg.webUrlArg,
|
|
|
|
|
optionsArg.getFunction
|
|
|
|
|
);
|
|
|
|
|
return moduleResult;
|
2020-09-29 18:38:21 +00:00
|
|
|
} else {
|
|
|
|
|
console.error('platform for loading not supported by smartenv');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-09 16:37:52 +01:00
|
|
|
public async getSafeNodeModule<T = any>(moduleNameArg: string, runAfterFunc?: (moduleArg: T) => Promise<any>): Promise<T> {
|
2025-11-01 15:26:26 +00:00
|
|
|
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.`);
|
2020-09-29 17:49:33 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2020-06-25 23:00:32 +00:00
|
|
|
// tslint:disable-next-line: function-constructor
|
2023-11-09 16:20:20 +01:00
|
|
|
const returnValue: T = await (new Function(`return import('${moduleNameArg}')`)() as Promise<T>);
|
|
|
|
|
if (runAfterFunc) {
|
|
|
|
|
await runAfterFunc(returnValue);
|
|
|
|
|
}
|
|
|
|
|
return returnValue;
|
2020-06-25 23:00:32 +00:00
|
|
|
}
|
|
|
|
|
|
2020-09-29 17:49:33 +00:00
|
|
|
public loadedScripts: string[] = [];
|
2020-09-29 18:38:21 +00:00
|
|
|
public async getSafeWebModule(urlArg: string, getFunctionArg: () => any) {
|
2020-09-29 17:49:33 +00:00
|
|
|
if (!this.isBrowser) {
|
|
|
|
|
console.error('You tried to load a web module in a wrong context');
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-12-28 19:43:48 +01:00
|
|
|
|
2020-09-29 17:49:33 +00:00
|
|
|
if (this.loadedScripts.includes(urlArg)) {
|
2020-10-13 20:09:15 +00:00
|
|
|
return getFunctionArg();
|
2020-09-29 17:49:33 +00:00
|
|
|
} else {
|
|
|
|
|
this.loadedScripts.push(urlArg);
|
|
|
|
|
}
|
2022-12-28 19:43:48 +01:00
|
|
|
|
2020-09-29 17:49:33 +00:00
|
|
|
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;
|
2020-09-29 18:38:21 +00:00
|
|
|
return getFunctionArg();
|
2020-09-29 17:49:33 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-01 15:26:26 +00:00
|
|
|
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') {
|
2019-06-17 08:46:28 +02:00
|
|
|
return 'node';
|
2025-11-01 15:26:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check Browser (default fallback)
|
|
|
|
|
if (typeof globalThis.window !== 'undefined' &&
|
|
|
|
|
typeof (globalThis as any).document !== 'undefined') {
|
2022-05-28 23:22:31 +02:00
|
|
|
return 'browser';
|
2018-02-14 00:12:21 +01:00
|
|
|
}
|
2025-11-01 15:26:26 +00:00
|
|
|
|
|
|
|
|
// Safe fallback
|
|
|
|
|
return 'browser';
|
2018-02-14 00:12:21 +01:00
|
|
|
}
|
|
|
|
|
|
2019-08-22 00:20:10 +02:00
|
|
|
public get isBrowser(): boolean {
|
2025-11-01 15:26:26 +00:00
|
|
|
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';
|
2018-02-14 00:12:21 +01:00
|
|
|
}
|
|
|
|
|
|
2019-08-22 00:20:10 +02:00
|
|
|
public get userAgent(): string {
|
2019-06-17 08:46:28 +02:00
|
|
|
if (this.isBrowser) {
|
|
|
|
|
// make sure we are in Browser
|
|
|
|
|
return navigator.userAgent;
|
2018-02-14 00:12:21 +01:00
|
|
|
} else {
|
2019-06-17 08:46:28 +02:00
|
|
|
return 'undefined';
|
2018-02-14 00:12:21 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-01 15:26:26 +00:00
|
|
|
public get nodeVersion(): string {
|
|
|
|
|
if (this.isNode) {
|
|
|
|
|
return process.version;
|
|
|
|
|
}
|
|
|
|
|
return 'undefined';
|
2018-02-14 00:12:21 +01:00
|
|
|
}
|
|
|
|
|
|
2025-11-01 15:26:26 +00:00
|
|
|
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;
|
2018-02-14 00:12:21 +01:00
|
|
|
}
|
2019-06-17 08:46:28 +02:00
|
|
|
|
2019-08-22 00:20:10 +02:00
|
|
|
public get isCI(): boolean {
|
2018-02-14 00:12:21 +01:00
|
|
|
if (this.isNode) {
|
|
|
|
|
if (process.env.CI) {
|
2019-06-17 08:46:28 +02:00
|
|
|
return true;
|
2018-02-14 00:12:21 +01:00
|
|
|
} else {
|
2019-06-17 08:46:28 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2018-02-14 00:12:21 +01:00
|
|
|
} else {
|
2019-06-17 08:46:28 +02:00
|
|
|
return false;
|
2018-02-14 00:12:21 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-22 00:20:10 +02:00
|
|
|
public async isMacAsync(): Promise<boolean> {
|
2019-06-17 08:46:28 +02:00
|
|
|
if (this.isNode) {
|
2022-03-16 16:09:24 +01:00
|
|
|
const os = await this.getSafeNodeModule('os');
|
2019-06-17 08:46:28 +02:00
|
|
|
return os.platform() === 'darwin';
|
2018-02-14 00:12:21 +01:00
|
|
|
} else {
|
2019-06-17 08:46:28 +02:00
|
|
|
return false;
|
2018-02-14 00:12:21 +01:00
|
|
|
}
|
|
|
|
|
}
|
2019-06-17 08:46:28 +02:00
|
|
|
|
2019-08-22 00:20:10 +02:00
|
|
|
public async isWindowsAsync(): Promise<boolean> {
|
2019-06-17 08:46:28 +02:00
|
|
|
if (this.isNode) {
|
2022-03-16 16:09:24 +01:00
|
|
|
const os = await this.getSafeNodeModule('os');
|
2019-06-17 08:46:28 +02:00
|
|
|
return os.platform() === 'win32';
|
2018-02-14 00:12:21 +01:00
|
|
|
} else {
|
2019-06-17 08:46:28 +02:00
|
|
|
return false;
|
2018-02-14 00:12:21 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-22 00:20:10 +02:00
|
|
|
public async isLinuxAsync(): Promise<boolean> {
|
2019-06-17 08:46:28 +02:00
|
|
|
if (this.isNode) {
|
2022-03-16 16:09:24 +01:00
|
|
|
const os = await this.getSafeNodeModule('os');
|
2019-06-17 08:46:28 +02:00
|
|
|
return os.platform() === 'linux';
|
2018-02-14 00:12:21 +01:00
|
|
|
} else {
|
2019-06-17 08:46:28 +02:00
|
|
|
return false;
|
2018-02-14 00:12:21 +01:00
|
|
|
}
|
|
|
|
|
}
|
2017-05-17 15:59:10 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* prints the environment to console
|
|
|
|
|
*/
|
2019-08-22 00:20:10 +02:00
|
|
|
public async printEnv() {
|
2017-05-17 15:59:10 +02:00
|
|
|
if (this.isNode) {
|
2019-06-17 08:46:28 +02:00
|
|
|
console.log('running on NODE');
|
2022-12-28 19:43:48 +01:00
|
|
|
console.log('node version is ' + this.nodeVersion);
|
2025-11-01 15:26:26 +00:00
|
|
|
} 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);
|
2017-05-17 15:59:10 +02:00
|
|
|
} else {
|
2019-06-17 08:46:28 +02:00
|
|
|
console.log('running on BROWSER');
|
|
|
|
|
console.log('browser is ' + this.userAgent);
|
2017-05-17 15:59:10 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|