Skip to content

Commit

Permalink
feat(esm): convert to using esm modules
Browse files Browse the repository at this point in the history
  • Loading branch information
toddbluhm committed Dec 2, 2024
1 parent ef9665c commit f8106ac
Show file tree
Hide file tree
Showing 33 changed files with 386 additions and 274 deletions.
8 changes: 8 additions & 0 deletions .mocharc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"$schema": "https://json.schemastore.org/mocharc.json",
"require": ["tsx/esm", "esmock"],
"extensions": ["ts"],
"spec": [
"test/**/*.ts"
]
}
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2019 Todd Bluhm
Copyright (c) Todd Bluhm

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
3 changes: 2 additions & 1 deletion bin/env-cmd.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
#! /usr/bin/env node
require('../dist').CLI(process.argv.slice(2))
import { CLI } from '../dist'
CLI(process.argv.slice(2))
47 changes: 21 additions & 26 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,38 @@
const eslint = require('@eslint/js')
const tseslint = require('typescript-eslint')
const globals = require('globals')
const stylistic = require('@stylistic/eslint-plugin')
import { default as tseslint } from 'typescript-eslint'
import { default as globals } from 'globals'
import { default as eslint } from '@eslint/js'

module.exports = tseslint.config(
export default tseslint.config(
{
ignores: ['dist/*', 'bin/*'],
rules: {
'@typescript-eslint/no-require-imports': 'off',
},
// Ignore build folder
ignores: ['dist/*'],
},
eslint.configs.recommended,
tseslint.configs.strictTypeChecked,
tseslint.configs.stylisticTypeChecked,
{
// Enable Type generation
languageOptions: {
globals: {
...globals.node,
},
parserOptions: {
projectService: {
allowDefaultProject: ['test/*.ts'],
},
project: ['./tsconfig.json', './test/tsconfig.json'],
},
},
extends: [
eslint.configs.recommended,
stylistic.configs['recommended-flat'],
tseslint.configs.strictTypeChecked,
tseslint.configs.stylisticTypeChecked,
],
},
// Disable Type Checking JS files
{
files: ['**/*.js'],
extends: [tseslint.configs.disableTypeChecked],
}
},
{
// For test files ignore some rules
files: ['test/*.ts'],
files: ['test/**/*'],
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-unsafe-member-access': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off'
},
},
// Disable Type Checking JS/CJS/MJS files
{
files: ['**/*.js', '**/*.cjs', '**/*.mjs'],
extends: [tseslint.configs.disableTypeChecked],
},
)
26 changes: 0 additions & 26 deletions eslint.config.js.test

This file was deleted.

62 changes: 19 additions & 43 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@
"description": "Executes a command using the environment variables in an env file",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"type": "module",
"engines": {
"node": ">=8.0.0"
"node": ">=18.0.0"
},
"bin": {
"env-cmd": "bin/env-cmd.js"
},
"scripts": {
"prepare": "husky",
"test": "mocha -r ts-node/register ./test/**/*.ts",
"test-cover": "nyc npm test",
"test": "mocha",
"test-cover": "c8 npm test",
"coveralls": "coveralls < coverage/lcov.info",
"lint": "npx eslint .",
"build": "tsc",
Expand Down Expand Up @@ -54,58 +55,33 @@
"devDependencies": {
"@commitlint/cli": "^19.6.0",
"@commitlint/config-conventional": "^19.6.0",
"@eslint/js": "^9.15.0",
"@stylistic/eslint-plugin": "^2.11.0",
"@types/chai": "^4.0.0",
"@types/cross-spawn": "^6.0.0",
"@types/mocha": "^7.0.0",
"@types/node": "^12.0.0",
"@eslint/js": "^9.16.0",
"@types/chai": "^5.0.1",
"@types/cross-spawn": "^6.0.6",
"@types/mocha": "^10.0.10",
"@types/node": "^22.10.1",
"@types/sinon": "^17.0.3",
"c8": "^10.1.2",
"chai": "^5.1.2",
"coveralls": "^3.0.0",
"esmock": "^2.6.9",
"globals": "^15.12.0",
"husky": "^9.1.7",
"mocha": "^10.8.2",
"nyc": "^17.1.0",
"mocha": "^11.0.0",
"sinon": "^19.0.2",
"ts-node": "^8.0.0",
"tsx": "^4.19.2",
"typescript": "^5.7.2",
"typescript-eslint": "^8.15.0"
},
"nyc": {
"include": [
"src/**/*.ts"
],
"extension": [
".ts"
],
"require": [
"ts-node/register"
],
"reporter": [
"text",
"lcov"
],
"sourceMap": true,
"instrument": true
},
"greenkeeper": {
"ignore": [
"@types/node"
],
"commitMessages": {
"initialBadge": "docs: add greenkeeper badge",
"initialDependencies": "chore: update dependencies",
"initialBranches": "chore: whitelist greenkeeper branches",
"dependencyUpdate": "chore: update dependency ${dependency}",
"devDependencyUpdate": "chore: update devDependecy ${dependency}",
"dependencyPin": "fix: pin dependency ${dependency}",
"devDependencyPin": "fix: pin devDependecy ${dependency}"
}
},
"commitlint": {
"extends": [
"@commitlint/config-conventional"
]
},
"c8": {
"reporter": [
"text",
"lcov"
]
}
}
25 changes: 25 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import * as processLib from 'node:process'
import type { Environment } from './types.ts'
import { EnvCmd } from './env-cmd.js'
import { parseArgs } from './parse-args.js'

/**
* Executes env - cmd using command line arguments
* @export
* @param {string[]} args Command line argument to pass in ['-f', './.env']
* @returns {Promise<Environment>}
*/
export async function CLI(args: string[]): Promise<Environment> {

// Parse the args from the command line
const parsedArgs = parseArgs(args)

// Run EnvCmd
try {
return await EnvCmd(parsedArgs)
}
catch (e) {
console.error(e)
return processLib.exit(1)
}
}
36 changes: 8 additions & 28 deletions src/env-cmd.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,9 @@
import { spawn } from './spawn'
import { EnvCmdOptions, Environment } from './types'
import { TermSignals } from './signal-termination'
import { parseArgs } from './parse-args'
import { getEnvVars } from './get-env-vars'
import { expandEnvs } from './expand-envs'

/**
* Executes env - cmd using command line arguments
* @export
* @param {string[]} args Command line argument to pass in ['-f', './.env']
* @returns {Promise<Environment>}
*/
export async function CLI(args: string[]): Promise<Environment> {
// Parse the args from the command line
const parsedArgs = parseArgs(args)

// Run EnvCmd
try {
return await (exports as { EnvCmd: typeof EnvCmd }).EnvCmd(parsedArgs)
}
catch (e) {
console.error(e)
return process.exit(1)
}
}
import { default as spawn } from 'cross-spawn'
import type { EnvCmdOptions, Environment } from './types.ts'
import { TermSignals } from './signal-termination.js'
import { getEnvVars } from './get-env-vars.js'
import { expandEnvs } from './expand-envs.js'
import * as processLib from 'node:process'

/**
* The main env-cmd program. This will spawn a new process and run the given command using
Expand Down Expand Up @@ -53,11 +33,11 @@ export async function EnvCmd(
}
// Override the merge order if --no-override flag set
if (options.noOverride === true) {
env = Object.assign({}, env, process.env)
env = Object.assign({}, env, processLib.env)
}
else {
// Add in the system environment variables to our environment list
env = Object.assign({}, process.env, env)
env = Object.assign({}, processLib.env, env)
}

if (options.expandEnvs === true) {
Expand Down
2 changes: 1 addition & 1 deletion src/expand-envs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Environment } from './types'
import type { Environment } from './types.ts'

/**
* expandEnvs Replaces $var in args and command with environment variables
Expand Down
6 changes: 3 additions & 3 deletions src/get-env-vars.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { GetEnvVarOptions, Environment } from './types'
import { getRCFileVars } from './parse-rc-file'
import { getEnvFileVars } from './parse-env-file'
import type { GetEnvVarOptions, Environment } from './types.ts'
import { getRCFileVars } from './parse-rc-file.js'
import { getEnvFileVars } from './parse-env-file.js'

const RC_FILE_DEFAULT_LOCATIONS = ['./.env-cmdrc', './.env-cmdrc.js', './.env-cmdrc.json']
const ENV_FILE_DEFAULT_LOCATIONS = ['./.env', './.env.js', './.env.json']
Expand Down
7 changes: 4 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { getEnvVars } from './get-env-vars'
import { getEnvVars } from './get-env-vars.js'

// Export the core env-cmd API
export * from './types'
export * from './env-cmd'
export * from './types.js'
export * from './cli.js'
export * from './env-cmd.js'
export const GetEnvVars = getEnvVars
6 changes: 3 additions & 3 deletions src/parse-args.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import * as commander from 'commander'
import { EnvCmdOptions, CommanderOptions, EnvFileOptions, RCFileOptions } from './types'
import { parseArgList } from './utils'
import type { EnvCmdOptions, CommanderOptions, EnvFileOptions, RCFileOptions } from './types.ts'
import { parseArgList } from './utils.js'

// Use commonjs require to prevent a weird folder hierarchy in dist
const packageJson: { version: string } = require('../package.json') /* eslint-disable-line */
const packageJson = (await import('../package.json')).default

/**
* Parses the arguments passed into the cli
Expand Down
20 changes: 13 additions & 7 deletions src/parse-env-file.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import * as fs from 'fs'
import * as path from 'path'
import { resolveEnvFilePath, isPromise } from './utils'
import { Environment } from './types'

const REQUIRE_HOOK_EXTENSIONS = ['.json', '.js', '.cjs']
import { resolveEnvFilePath, IMPORT_HOOK_EXTENSIONS, isPromise } from './utils.js'
import type { Environment } from './types.ts'

/**
* Gets the environment vars from an env file
Expand All @@ -19,9 +17,17 @@ export async function getEnvFileVars(envFilePath: string): Promise<Environment>
// Get the file extension
const ext = path.extname(absolutePath).toLowerCase()
let env: Environment = {}
if (REQUIRE_HOOK_EXTENSIONS.includes(ext)) {
const possiblePromise: Environment | Promise<Environment> = require(absolutePath) /* eslint-disable-line */
env = isPromise(possiblePromise) ? await possiblePromise : possiblePromise
if (IMPORT_HOOK_EXTENSIONS.includes(ext)) {
const res = await import(absolutePath) as Environment | { default: Environment }
if ('default' in res) {
env = res.default as Environment
} else {
env = res
}
// Check to see if the imported value is a promise
if (isPromise(env)) {
env = await env
}
}
else {
const file = fs.readFileSync(absolutePath, { encoding: 'utf8' })
Expand Down
20 changes: 14 additions & 6 deletions src/parse-rc-file.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { stat, readFile } from 'fs'
import { promisify } from 'util'
import { extname } from 'path'
import { resolveEnvFilePath, isPromise } from './utils'
import { Environment, RCEnvironment } from './types'
import { resolveEnvFilePath, IMPORT_HOOK_EXTENSIONS, isPromise } from './utils.js'
import type { Environment, RCEnvironment } from './types.ts'

const statAsync = promisify(stat)
const readFileAsync = promisify(readFile)
Expand All @@ -26,11 +26,19 @@ export async function getRCFileVars(

// Get the file extension
const ext = extname(absolutePath).toLowerCase()
let parsedData: Partial<RCEnvironment>
let parsedData: Partial<RCEnvironment> = {}
try {
if (ext === '.json' || ext === '.js' || ext === '.cjs') {
const possiblePromise = require(absolutePath) as PromiseLike<RCEnvironment> | RCEnvironment
parsedData = isPromise(possiblePromise) ? await possiblePromise : possiblePromise
if (IMPORT_HOOK_EXTENSIONS.includes(ext)) {
const res = await import(absolutePath) as RCEnvironment | { default: RCEnvironment }
if ('default' in res) {
parsedData = res.default as RCEnvironment
} else {
parsedData = res
}
// Check to see if the imported value is a promise
if (isPromise(parsedData)) {
parsedData = await parsedData
}
}
else {
const file = await readFileAsync(absolutePath, { encoding: 'utf8' })
Expand Down
4 changes: 0 additions & 4 deletions src/spawn.ts

This file was deleted.

Loading

0 comments on commit f8106ac

Please sign in to comment.