mirror of
https://github.com/toddbluhm/env-cmd.git
synced 2025-12-08 18:23:33 +00:00
feat: support loading TypeScript files
This commit is contained in:
parent
642c451d08
commit
b314da93c5
6
dist/get-env-vars.js
vendored
6
dist/get-env-vars.js
vendored
@ -1,5 +1,6 @@
|
|||||||
import { getRCFileVars } from './parse-rc-file.js';
|
import { getRCFileVars } from './parse-rc-file.js';
|
||||||
import { getEnvFileVars } from './parse-env-file.js';
|
import { getEnvFileVars } from './parse-env-file.js';
|
||||||
|
import { isLoaderError } from './utils.js';
|
||||||
const RC_FILE_DEFAULT_LOCATIONS = ['./.env-cmdrc', './.env-cmdrc.js', './.env-cmdrc.json'];
|
const RC_FILE_DEFAULT_LOCATIONS = ['./.env-cmdrc', './.env-cmdrc.js', './.env-cmdrc.json'];
|
||||||
const ENV_FILE_DEFAULT_LOCATIONS = ['./.env', './.env.js', './.env.json'];
|
const ENV_FILE_DEFAULT_LOCATIONS = ['./.env', './.env.js', './.env.json'];
|
||||||
export async function getEnvVars(options = {}) {
|
export async function getEnvVars(options = {}) {
|
||||||
@ -28,7 +29,10 @@ export async function getEnvFile({ filePath, fallback, verbose }) {
|
|||||||
}
|
}
|
||||||
return env;
|
return env;
|
||||||
}
|
}
|
||||||
catch {
|
catch (error) {
|
||||||
|
if (isLoaderError(error)) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
if (verbose === true) {
|
if (verbose === true) {
|
||||||
console.info(`Failed to find .env file at path: ${filePath}`);
|
console.info(`Failed to find .env file at path: ${filePath}`);
|
||||||
}
|
}
|
||||||
|
|||||||
1
dist/loaders/typescript.d.ts
vendored
Normal file
1
dist/loaders/typescript.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export declare function checkIfTypescriptSupported(): void;
|
||||||
8
dist/loaders/typescript.js
vendored
Normal file
8
dist/loaders/typescript.js
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export function checkIfTypescriptSupported() {
|
||||||
|
if (!process.features.typescript) {
|
||||||
|
const error = new Error('To load typescript files with env-cmd, you need to upgrade to node v23.6' +
|
||||||
|
' or later. See https://nodejs.org/en/learn/typescript/run-natively');
|
||||||
|
Object.assign(error, { code: 'ERR_UNKNOWN_FILE_EXTENSION' });
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
3
dist/parse-env-file.js
vendored
3
dist/parse-env-file.js
vendored
@ -2,6 +2,7 @@ import { existsSync, readFileSync } from 'node:fs';
|
|||||||
import { extname } from 'node:path';
|
import { extname } from 'node:path';
|
||||||
import { pathToFileURL } from 'node:url';
|
import { pathToFileURL } from 'node:url';
|
||||||
import { resolveEnvFilePath, IMPORT_HOOK_EXTENSIONS, isPromise } from './utils.js';
|
import { resolveEnvFilePath, IMPORT_HOOK_EXTENSIONS, isPromise } from './utils.js';
|
||||||
|
import { checkIfTypescriptSupported } from './loaders/typescript.js';
|
||||||
/**
|
/**
|
||||||
* Gets the environment vars from an env file
|
* Gets the environment vars from an env file
|
||||||
*/
|
*/
|
||||||
@ -16,6 +17,8 @@ export async function getEnvFileVars(envFilePath) {
|
|||||||
const ext = extname(absolutePath).toLowerCase();
|
const ext = extname(absolutePath).toLowerCase();
|
||||||
let env;
|
let env;
|
||||||
if (IMPORT_HOOK_EXTENSIONS.includes(ext)) {
|
if (IMPORT_HOOK_EXTENSIONS.includes(ext)) {
|
||||||
|
if (/tsx?$/.test(ext))
|
||||||
|
checkIfTypescriptSupported();
|
||||||
// For some reason in ES Modules, only JSON file types need to be specifically delinated when importing them
|
// For some reason in ES Modules, only JSON file types need to be specifically delinated when importing them
|
||||||
let attributeTypes = {};
|
let attributeTypes = {};
|
||||||
if (ext === '.json') {
|
if (ext === '.json') {
|
||||||
|
|||||||
3
dist/parse-rc-file.js
vendored
3
dist/parse-rc-file.js
vendored
@ -3,6 +3,7 @@ import { promisify } from 'node:util';
|
|||||||
import { extname } from 'node:path';
|
import { extname } from 'node:path';
|
||||||
import { pathToFileURL } from 'node:url';
|
import { pathToFileURL } from 'node:url';
|
||||||
import { resolveEnvFilePath, IMPORT_HOOK_EXTENSIONS, isPromise } from './utils.js';
|
import { resolveEnvFilePath, IMPORT_HOOK_EXTENSIONS, isPromise } from './utils.js';
|
||||||
|
import { checkIfTypescriptSupported } from './loaders/typescript.js';
|
||||||
const statAsync = promisify(stat);
|
const statAsync = promisify(stat);
|
||||||
const readFileAsync = promisify(readFile);
|
const readFileAsync = promisify(readFile);
|
||||||
/**
|
/**
|
||||||
@ -23,6 +24,8 @@ export async function getRCFileVars({ environments, filePath }) {
|
|||||||
let parsedData = {};
|
let parsedData = {};
|
||||||
try {
|
try {
|
||||||
if (IMPORT_HOOK_EXTENSIONS.includes(ext)) {
|
if (IMPORT_HOOK_EXTENSIONS.includes(ext)) {
|
||||||
|
if (/tsx?$/.test(ext))
|
||||||
|
checkIfTypescriptSupported();
|
||||||
// For some reason in ES Modules, only JSON file types need to be specifically delinated when importing them
|
// For some reason in ES Modules, only JSON file types need to be specifically delinated when importing them
|
||||||
let attributeTypes = {};
|
let attributeTypes = {};
|
||||||
if (ext === '.json') {
|
if (ext === '.json') {
|
||||||
|
|||||||
2
dist/utils.d.ts
vendored
2
dist/utils.d.ts
vendored
@ -11,3 +11,5 @@ export declare function parseArgList(list: string): string[];
|
|||||||
* A simple function to test if the value is a promise/thenable
|
* A simple function to test if the value is a promise/thenable
|
||||||
*/
|
*/
|
||||||
export declare function isPromise<T>(value?: T | PromiseLike<T>): value is PromiseLike<T>;
|
export declare function isPromise<T>(value?: T | PromiseLike<T>): value is PromiseLike<T>;
|
||||||
|
/** @returns true if the error is `ERR_UNKNOWN_FILE_EXTENSION` */
|
||||||
|
export declare function isLoaderError(error: unknown): error is Error;
|
||||||
|
|||||||
17
dist/utils.js
vendored
17
dist/utils.js
vendored
@ -2,7 +2,16 @@ import { resolve } from 'node:path';
|
|||||||
import { homedir } from 'node:os';
|
import { homedir } from 'node:os';
|
||||||
import { cwd } from 'node:process';
|
import { cwd } from 'node:process';
|
||||||
// Special file extensions that node can natively import
|
// Special file extensions that node can natively import
|
||||||
export const IMPORT_HOOK_EXTENSIONS = ['.json', '.js', '.cjs', '.mjs'];
|
export const IMPORT_HOOK_EXTENSIONS = [
|
||||||
|
'.json',
|
||||||
|
'.js',
|
||||||
|
'.cjs',
|
||||||
|
'.mjs',
|
||||||
|
'.ts',
|
||||||
|
'.mts',
|
||||||
|
'.cts',
|
||||||
|
'.tsx',
|
||||||
|
];
|
||||||
/**
|
/**
|
||||||
* A simple function for resolving the path the user entered
|
* A simple function for resolving the path the user entered
|
||||||
*/
|
*/
|
||||||
@ -29,3 +38,9 @@ export function isPromise(value) {
|
|||||||
&& 'then' in value
|
&& 'then' in value
|
||||||
&& typeof value.then === 'function';
|
&& typeof value.then === 'function';
|
||||||
}
|
}
|
||||||
|
/** @returns true if the error is `ERR_UNKNOWN_FILE_EXTENSION` */
|
||||||
|
export function isLoaderError(error) {
|
||||||
|
return (error instanceof Error &&
|
||||||
|
'code' in error &&
|
||||||
|
error.code === 'ERR_UNKNOWN_FILE_EXTENSION');
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import type { GetEnvVarOptions, Environment } from './types.ts'
|
import type { GetEnvVarOptions, Environment } from './types.ts'
|
||||||
import { getRCFileVars } from './parse-rc-file.js'
|
import { getRCFileVars } from './parse-rc-file.js'
|
||||||
import { getEnvFileVars } from './parse-env-file.js'
|
import { getEnvFileVars } from './parse-env-file.js'
|
||||||
|
import { isLoaderError } from './utils.js'
|
||||||
|
|
||||||
const RC_FILE_DEFAULT_LOCATIONS = ['./.env-cmdrc', './.env-cmdrc.js', './.env-cmdrc.json']
|
const RC_FILE_DEFAULT_LOCATIONS = ['./.env-cmdrc', './.env-cmdrc.js', './.env-cmdrc.json']
|
||||||
const ENV_FILE_DEFAULT_LOCATIONS = ['./.env', './.env.js', './.env.json']
|
const ENV_FILE_DEFAULT_LOCATIONS = ['./.env', './.env.js', './.env.json']
|
||||||
@ -34,7 +35,11 @@ export async function getEnvFile(
|
|||||||
}
|
}
|
||||||
return env
|
return env
|
||||||
}
|
}
|
||||||
catch {
|
catch (error) {
|
||||||
|
if (isLoaderError(error)) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
if (verbose === true) {
|
if (verbose === true) {
|
||||||
console.info(`Failed to find .env file at path: ${filePath}`)
|
console.info(`Failed to find .env file at path: ${filePath}`)
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/loaders/typescript.ts
Normal file
10
src/loaders/typescript.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export function checkIfTypescriptSupported() {
|
||||||
|
if (!process.features.typescript) {
|
||||||
|
const error = new Error(
|
||||||
|
'To load typescript files with env-cmd, you need to upgrade to node v23.6' +
|
||||||
|
' or later. See https://nodejs.org/en/learn/typescript/run-natively',
|
||||||
|
);
|
||||||
|
Object.assign(error, { code: 'ERR_UNKNOWN_FILE_EXTENSION' });
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@ import { extname } from 'node:path'
|
|||||||
import { pathToFileURL } from 'node:url'
|
import { pathToFileURL } from 'node:url'
|
||||||
import { resolveEnvFilePath, IMPORT_HOOK_EXTENSIONS, isPromise } from './utils.js'
|
import { resolveEnvFilePath, IMPORT_HOOK_EXTENSIONS, isPromise } from './utils.js'
|
||||||
import type { Environment } from './types.ts'
|
import type { Environment } from './types.ts'
|
||||||
|
import { checkIfTypescriptSupported } from './loaders/typescript.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the environment vars from an env file
|
* Gets the environment vars from an env file
|
||||||
@ -19,6 +20,8 @@ export async function getEnvFileVars(envFilePath: string): Promise<Environment>
|
|||||||
const ext = extname(absolutePath).toLowerCase()
|
const ext = extname(absolutePath).toLowerCase()
|
||||||
let env: unknown;
|
let env: unknown;
|
||||||
if (IMPORT_HOOK_EXTENSIONS.includes(ext)) {
|
if (IMPORT_HOOK_EXTENSIONS.includes(ext)) {
|
||||||
|
if (/tsx?$/.test(ext)) checkIfTypescriptSupported();
|
||||||
|
|
||||||
// For some reason in ES Modules, only JSON file types need to be specifically delinated when importing them
|
// For some reason in ES Modules, only JSON file types need to be specifically delinated when importing them
|
||||||
let attributeTypes = {}
|
let attributeTypes = {}
|
||||||
if (ext === '.json') {
|
if (ext === '.json') {
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { extname } from 'node:path'
|
|||||||
import { pathToFileURL } from 'node:url'
|
import { pathToFileURL } from 'node:url'
|
||||||
import { resolveEnvFilePath, IMPORT_HOOK_EXTENSIONS, isPromise } from './utils.js'
|
import { resolveEnvFilePath, IMPORT_HOOK_EXTENSIONS, isPromise } from './utils.js'
|
||||||
import type { Environment, RCEnvironment } from './types.ts'
|
import type { Environment, RCEnvironment } from './types.ts'
|
||||||
|
import { checkIfTypescriptSupported } from './loaders/typescript.js'
|
||||||
|
|
||||||
const statAsync = promisify(stat)
|
const statAsync = promisify(stat)
|
||||||
const readFileAsync = promisify(readFile)
|
const readFileAsync = promisify(readFile)
|
||||||
@ -30,6 +31,8 @@ export async function getRCFileVars(
|
|||||||
let parsedData: Partial<RCEnvironment> = {}
|
let parsedData: Partial<RCEnvironment> = {}
|
||||||
try {
|
try {
|
||||||
if (IMPORT_HOOK_EXTENSIONS.includes(ext)) {
|
if (IMPORT_HOOK_EXTENSIONS.includes(ext)) {
|
||||||
|
if (/tsx?$/.test(ext)) checkIfTypescriptSupported()
|
||||||
|
|
||||||
// For some reason in ES Modules, only JSON file types need to be specifically delinated when importing them
|
// For some reason in ES Modules, only JSON file types need to be specifically delinated when importing them
|
||||||
let attributeTypes = {}
|
let attributeTypes = {}
|
||||||
if (ext === '.json') {
|
if (ext === '.json') {
|
||||||
|
|||||||
20
src/utils.ts
20
src/utils.ts
@ -3,7 +3,16 @@ import { homedir } from 'node:os'
|
|||||||
import { cwd } from 'node:process'
|
import { cwd } from 'node:process'
|
||||||
|
|
||||||
// Special file extensions that node can natively import
|
// Special file extensions that node can natively import
|
||||||
export const IMPORT_HOOK_EXTENSIONS = ['.json', '.js', '.cjs', '.mjs']
|
export const IMPORT_HOOK_EXTENSIONS = [
|
||||||
|
'.json',
|
||||||
|
'.js',
|
||||||
|
'.cjs',
|
||||||
|
'.mjs',
|
||||||
|
'.ts',
|
||||||
|
'.mts',
|
||||||
|
'.cts',
|
||||||
|
'.tsx',
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple function for resolving the path the user entered
|
* A simple function for resolving the path the user entered
|
||||||
@ -32,3 +41,12 @@ export function isPromise<T>(value?: T | PromiseLike<T>): value is PromiseLike<T
|
|||||||
&& 'then' in value
|
&& 'then' in value
|
||||||
&& typeof value.then === 'function'
|
&& typeof value.then === 'function'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @returns true if the error is `ERR_UNKNOWN_FILE_EXTENSION` */
|
||||||
|
export function isLoaderError(error: unknown): error is Error {
|
||||||
|
return (
|
||||||
|
error instanceof Error &&
|
||||||
|
'code' in error &&
|
||||||
|
error.code === 'ERR_UNKNOWN_FILE_EXTENSION'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@ -192,6 +192,32 @@ describe('getEnvFileVars', (): void => {
|
|||||||
THANKS: 'FOR ALL THE FISH',
|
THANKS: 'FOR ALL THE FISH',
|
||||||
ANSWER: '0',
|
ANSWER: '0',
|
||||||
})
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
(process.features.typescript ? describe : describe.skip)('TS', () => {
|
||||||
|
it('should parse a .ts file', async () => {
|
||||||
|
const env = await getEnvFileVars('./test/test-files/ts-test.ts');
|
||||||
|
assert.deepEqual(env, {
|
||||||
|
THANKS: 'FOR ALL THE FISH',
|
||||||
|
ANSWER: '1',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse a .cts file', async () => {
|
||||||
|
const env = await getEnvFileVars('./test/test-files/cts-test.cts');
|
||||||
|
assert.deepEqual(env, {
|
||||||
|
THANKS: 'FOR ALL THE FISH',
|
||||||
|
ANSWER: '0',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse a .tsx file', async () => {
|
||||||
|
const env = await getEnvFileVars('./test/test-files/tsx-test.tsx');
|
||||||
|
assert.deepEqual(env, {
|
||||||
|
THANKS: 'FOR ALL THE FISH',
|
||||||
|
ANSWER: '2',
|
||||||
|
});
|
||||||
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should parse an env file', async (): Promise<void> => {
|
it('should parse an env file', async (): Promise<void> => {
|
||||||
|
|||||||
5
test/test-files/cts-test.cts
Normal file
5
test/test-files/cts-test.cts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
const env: unknown = {
|
||||||
|
THANKS: 'FOR ALL THE FISH',
|
||||||
|
ANSWER: 0,
|
||||||
|
};
|
||||||
|
export default env;
|
||||||
7
test/test-files/ts-test.ts
Normal file
7
test/test-files/ts-test.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import type { Environment } from '../../src/types.js';
|
||||||
|
|
||||||
|
const env: Environment = {
|
||||||
|
THANKS: 'FOR ALL THE FISH',
|
||||||
|
ANSWER: 1,
|
||||||
|
};
|
||||||
|
export default env;
|
||||||
8
test/test-files/tsx-test.tsx
Normal file
8
test/test-files/tsx-test.tsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import type { Environment } from '../../src/types.js';
|
||||||
|
|
||||||
|
const env: Environment = {
|
||||||
|
THANKS: 'FOR ALL THE FISH',
|
||||||
|
ANSWER: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default env;
|
||||||
Loading…
x
Reference in New Issue
Block a user