Skip to content
Merged

Beta #14

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
# EnvTypesWebpack

<center>

<img src="./icon.png" width="50%" />

</center>
<center>

[![npm version](https://img.shields.io/npm/v/@xxanderwp/env-types-webpack-plugin.svg)](https://www.npmjs.com/package/EnvTypesWebpack)
[![npm version](https://img.shields.io/npm/v/@xxanderwp/env-types-webpack-plugin.svg)](https://www.npmjs.com/package/@xxanderwp/env-types-webpack-plugin)
![NPM Downloads](https://img.shields.io/npm/dm/%40xxanderwp%2Fenv-types-webpack-plugin)
[![Tests](https://github.com/XXanderWP/EnvTypesWebpack/workflows/Tests/badge.svg)](https://github.com/XXanderWP/EnvTypesWebpack/actions)
[![license](https://img.shields.io/github/license/XXanderWP/EnvTypesWebpack.svg)](https://github.com/XXanderWP/EnvTypesWebpack/blob/main/LICENSE)

</center>


Webpack plugin that automatically generates TypeScript definitions for environment variables from `.env` files.

## Features
Expand Down Expand Up @@ -132,6 +140,7 @@ const apiKey = process.env.API_KEY; // Type: string | undefined
| `addExportEnds` | `boolean` | `false` | Add `export {};` at end |
| `namespace` | `string` | `NodeJS` | Optional namespace for the generated types |
| `interface` | `string` | `ProcessEnv` | Optional interface name for the generated types |
| `useValuesAsTypes` | `boolean` | `false` | Use values as types instead of string literals |

#### Shorthand

Expand Down
Binary file added icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@xxanderwp/env-types-webpack-plugin",
"version": "1.3.1",
"version": "1.4.0",
"description": "Webpack plugin that automatically generates TypeScript definitions for environment variables from .env files",
"main": "dist/EnvTypesPlugin.js",
"types": "dist/EnvTypesPlugin.d.ts",
Expand Down
23 changes: 20 additions & 3 deletions src/EnvTypesGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export interface EnvEntry {
key: string;
/** JSDoc comment describing the variable */
comment: string | null;
/** Value for useValuesAsTypes mode */
value: string;
}

/**
Expand All @@ -30,6 +32,7 @@ export class EnvTypesGenerator {
private readonly addExportEnds: boolean;
private readonly interface: string;
private readonly namespace: string;
private readonly useValuesAsTypes: boolean;

constructor(options: EnvTypesGeneratorOptions) {
this.envFiles = options.envFiles || ['.env', '.env.example'];
Expand All @@ -39,6 +42,7 @@ export class EnvTypesGenerator {
this.addExportEnds = options.addExportEnds ?? false;
this.interface = options.interface || 'ProcessEnv';
this.namespace = options.namespace || 'NodeJS';
this.useValuesAsTypes = options.useValuesAsTypes ?? false;
}

/**
Expand Down Expand Up @@ -103,6 +107,7 @@ export class EnvTypesGenerator {
entries.push({
key,
comment: comments.length ? comments.join('\n') : null,
value: valuePart.split('#')[0].trim(),
});

commentBuffer = [];
Expand All @@ -128,13 +133,23 @@ export class EnvTypesGenerator {
*/
private generateInterfaceBody(entries: EnvEntry[]): string {
return entries
.map(({ key, comment }) => {
.map(({ key, comment, value }) => {
// Определяем тип или конкретное значение
let typeOrValue: string;
if (this.useValuesAsTypes) {
typeOrValue = `"${value.replace(/"/g, '\\"')}"`;
} else {
typeOrValue = 'string';
}

const optional = this.disablePartialType ? '' : '?';

if (!comment) {
return ` ${key}${this.disablePartialType ? '' : '?'}: string;`;
return ` ${key}${optional}: ${typeOrValue};`;
}

const jsdoc = this.generateJSDoc(comment);
return `${jsdoc}\n ${key}${this.disablePartialType ? '' : '?'}: string;`;
return `${jsdoc}\n ${key}${optional}: ${typeOrValue};`;
})
.join('\n');
}
Expand Down Expand Up @@ -240,6 +255,7 @@ if (require.main === module) {
const addExportEnds = process.env.ADD_END === '1';
const interfaceName = process.env.INTERFACE || 'ProcessEnv';
const namespace = process.env.NAMESPACE || 'NodeJS';
const useValuesAsTypes = process.env.USE_VALUES_AS_TYPES === '1';

if (!outputPath) {
console.error(
Expand All @@ -256,6 +272,7 @@ if (require.main === module) {
addExportEnds,
interface: interfaceName,
namespace,
useValuesAsTypes,
});
generator.generate();
}
Expand Down
2 changes: 2 additions & 0 deletions src/EnvTypesPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export class EnvTypesPlugin {
addExportEnds: options.addExportEnds || false,
interface: options.interface || 'ProcessEnv',
namespace: options.namespace || 'NodeJS',
useValuesAsTypes: options.useValuesAsTypes || false,
};

this.outputAbsolutePath = path.resolve(this.options.outputPath);
Expand Down Expand Up @@ -101,6 +102,7 @@ export class EnvTypesPlugin {
ADD_END: this.options.addExportEnds ? '1' : '0',
INTERFACE: this.options.interface,
NAMESPACE: this.options.namespace,
USE_VALUES_AS_TYPES: this.options.useValuesAsTypes ? '1' : '0',
};

execSync(`node ${this.options.generatorScript}`, {
Expand Down
5 changes: 5 additions & 0 deletions src/types/EnvTypesGenerator.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,9 @@ export interface EnvTypesGeneratorOptions {
* @default "ProcessEnv"
*/
interface?: string;
/** Use values as types instead of string literals
* @default false
* If true, the generated types will use the actual values from the .env files as types instead of string literals. For example, if you have a variable `API_URL=http://localhost:3000`, the generated type will be `API_URL: "http://localhost:3000"` instead of `API_URL: string`.
*/
useValuesAsTypes?: boolean;
}
36 changes: 36 additions & 0 deletions tests/EnvTypesGenerator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ describe('EnvTypesGenerator', () => {
describe('constructor', () => {
it('should create instance with valid options', () => {
const generator = new EnvTypesGenerator({
silent: true,
envFiles: ['.env'],
outputPath: 'test.d.ts',
});
Expand All @@ -44,6 +45,7 @@ API_KEY=secret
fs.writeFileSync(envFile, envContent);

const generator = new EnvTypesGenerator({
silent: true,
envFiles: [envFile],
outputPath: outputFile,
});
Expand Down Expand Up @@ -75,6 +77,7 @@ DB_PORT=5432
fs.writeFileSync(envFile, envContent);

const generator = new EnvTypesGenerator({
silent: true,
envFiles: [envFile],
outputPath: outputFile,
});
Expand All @@ -97,6 +100,7 @@ DB_PORT=5432
fs.writeFileSync(envFile, envContent);

const generator = new EnvTypesGenerator({
silent: true,
envFiles: [envFile],
outputPath: outputFile,
});
Expand All @@ -117,6 +121,7 @@ DB_PORT=5432
fs.writeFileSync(envFile, envContent);

const generator = new EnvTypesGenerator({
silent: true,
envFiles: [envFile],
outputPath: outputFile,
});
Expand All @@ -142,6 +147,7 @@ DB_HOST=localhost
fs.writeFileSync(envFile, envContent);

const generator = new EnvTypesGenerator({
silent: true,
envFiles: [envFile],
outputPath: outputFile,
});
Expand Down Expand Up @@ -193,6 +199,7 @@ DB_HOST=localhost
fs.writeFileSync(envFile, 'DB_HOST=localhost');

const generator = new EnvTypesGenerator({
silent: true,
envFiles: [envFile],
outputPath: nestedOutput,
});
Expand All @@ -207,6 +214,7 @@ DB_HOST=localhost
it('should throw error if no env file found', () => {
// Arrange
const generator = new EnvTypesGenerator({
silent: true,
envFiles: ['.env.nonexistent'],
outputPath: outputFile,
});
Expand All @@ -224,6 +232,7 @@ DB_HOST=localhost
fs.writeFileSync(env2, 'PROD_VAR=prod');

const generator = new EnvTypesGenerator({
silent: true,
envFiles: [env1, env2],
outputPath: outputFile,
});
Expand Down Expand Up @@ -263,13 +272,38 @@ DB_HOST=localhost
expect(content).toContain('Line 3 of comment');
});

it('should handle useValuesAsTypes option', () => {
// Arrange
const envContent = `
DB_HOST=localhost
`.trim();

fs.writeFileSync(envFile, envContent);

const generator = new EnvTypesGenerator({
silent: true,
envFiles: [envFile],
outputPath: outputFile,
useValuesAsTypes: true,
});

// Act
generator.generate();

// Assert
const content = fs.readFileSync(outputFile, 'utf-8');
expect(content).toContain('DB_HOST');
expect(content).toContain('"localhost"');
});

it('should handle values with equals signs', () => {
// Arrange
const envContent = 'CONNECTION_STRING=host=localhost;port=5432';

fs.writeFileSync(envFile, envContent);

const generator = new EnvTypesGenerator({
silent: true,
envFiles: [envFile],
outputPath: outputFile,
});
Expand All @@ -287,6 +321,7 @@ DB_HOST=localhost
fs.writeFileSync(envFile, 'DB_HOST=localhost');

const generator = new EnvTypesGenerator({
silent: true,
envFiles: [envFile],
outputPath: outputFile,
});
Expand All @@ -304,6 +339,7 @@ DB_HOST=localhost
fs.writeFileSync(envFile, 'DB_HOST=localhost');

const generator = new EnvTypesGenerator({
silent: true,
envFiles: [envFile],
outputPath: outputFile,
});
Expand Down