Skip to content

Commit 170eec4

Browse files
🚀 release: v2.3.0 (#29)
* 📦 new: add bundle size checker with tests and workflow (#27) * Initial plan * 📦 new: add bundle size checker with tests and workflow Co-authored-by: warengonzaga <15052701+warengonzaga@users.noreply.github.com> * 📖 docs: add bundle size checker to documentation Co-authored-by: warengonzaga <15052701+warengonzaga@users.noreply.github.com> * 🧪 test: fix inverted logic in size checker test Co-authored-by: warengonzaga <15052701+warengonzaga@users.noreply.github.com> * 🔒 security: add permissions block to size-check workflow Co-authored-by: warengonzaga <15052701+warengonzaga@users.noreply.github.com> * ⚙️ setup: fix linting issues in size-checker test Co-authored-by: warengonzaga <15052701+warengonzaga@users.noreply.github.com> * 🔧 update: address PR review feedback Co-authored-by: warengonzaga <15052701+warengonzaga@users.noreply.github.com> * 🔧 update: improve error handling and validation Co-authored-by: warengonzaga <15052701+warengonzaga@users.noreply.github.com> * 🔒 security: fix shell injection and improve error visibility Co-authored-by: warengonzaga <15052701+warengonzaga@users.noreply.github.com> * 🔒 security: fix non-literal require in tests Co-authored-by: warengonzaga <15052701+warengonzaga@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: warengonzaga <15052701+warengonzaga@users.noreply.github.com> * 📦 new (emoji): add emoji selector and context-aware logic (#26) * Initial plan * 📦 new (emoji): add emoji selector and context-aware logic Co-authored-by: warengonzaga <15052701+warengonzaga@users.noreply.github.com> * 📖 docs: add comprehensive emoji feature documentation Co-authored-by: warengonzaga <15052701+warengonzaga@users.noreply.github.com> * 🔧 update: fix linting issues in emoji feature Co-authored-by: warengonzaga <15052701+warengonzaga@users.noreply.github.com> * 🔧 update (emoji): address code review feedback Co-authored-by: warengonzaga <15052701+warengonzaga@users.noreply.github.com> * 🔧 update: enable emoji by default and move toggle to format.includeEmoji Co-authored-by: warengonzaga <15052701+warengonzaga@users.noreply.github.com> * 📖 docs: update README for new emoji configuration pattern Co-authored-by: warengonzaga <15052701+warengonzaga@users.noreply.github.com> * 🔧 update: fix trailing spaces in tests Co-authored-by: warengonzaga <15052701+warengonzaga@users.noreply.github.com> * 🔧 update (emoji): address code review feedback on performance and state management Co-authored-by: warengonzaga <15052701+warengonzaga@users.noreply.github.com> * 🔧 update (emoji): move config to Logger.configure() to preserve regex cache Co-authored-by: warengonzaga <15052701+warengonzaga@users.noreply.github.com> * 🔧 update: fix eslint errors by removing invalid security rule comments Co-authored-by: warengonzaga <15052701+warengonzaga@users.noreply.github.com> * 🔒 security: add eslint-disable comments for safe code patterns Co-authored-by: warengonzaga <15052701+warengonzaga@users.noreply.github.com> * � test: improve patch coverage for emoji feature - Remove deprecated dead-code methods (getMappings, matchesKeywords) from EmojiSelector - Add tests for unknown log level edge case in EmojiSelector - Add tests for Logger.configure emoji configuration path - Add tests for numeric and boolean data edge cases * 🧪 test: improve patch coverage for emoji feature - Remove deprecated dead-code methods (getMappings, matchesKeywords) from EmojiSelector - Add tests for unknown log level edge case in EmojiSelector - Add tests for Logger.configure emoji configuration path - Add tests for numeric and boolean data edge cases * 🗑️ remove: delete outdated Snyk security instructions * 🔧 update: address code review feedback on performance and safety Co-authored-by: warengonzaga <15052701+warengonzaga@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: warengonzaga <15052701+warengonzaga@users.noreply.github.com> Co-authored-by: Waren Gonzaga <opensource@warengonzaga.com> * 🚀 release: bump version to 2.3.0 * 🗑️ remove: delete repository banner image * 📖 docs: update README with new features and improved descriptions * 📖 docs: update README to format code blocks for emoji log examples --------- Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: warengonzaga <15052701+warengonzaga@users.noreply.github.com>
1 parent c977791 commit 170eec4

19 files changed

+1792
-81
lines changed

.github/assets/repo_banner.jpg

-63.1 KB
Binary file not shown.

.github/workflows/size-check.yml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
name: Bundle Size Check
2+
3+
on:
4+
push:
5+
branches: [main, dev]
6+
pull_request:
7+
branches: [main, dev]
8+
9+
jobs:
10+
size-check:
11+
name: Check Bundle Size
12+
runs-on: ubuntu-latest
13+
permissions:
14+
contents: read
15+
16+
steps:
17+
- name: Checkout code
18+
uses: actions/checkout@v4
19+
20+
- name: Setup Node.js
21+
uses: actions/setup-node@v4
22+
with:
23+
node-version: '20'
24+
25+
- name: Enable Corepack for pnpm
26+
run: |
27+
echo "Enabling Corepack..."
28+
corepack enable
29+
echo "Preparing pnpm 9.15.4..."
30+
corepack prepare pnpm@9.15.4 --activate
31+
echo "pnpm setup complete"
32+
33+
- name: Verify pnpm version
34+
run: |
35+
echo "pnpm version:"
36+
pnpm --version
37+
38+
- name: Install dependencies
39+
run: |
40+
echo "Installing dependencies with pnpm..."
41+
pnpm install --frozen-lockfile
42+
echo "Dependencies installed successfully"
43+
44+
- name: Build package
45+
run: |
46+
echo "Building package..."
47+
pnpm build
48+
echo "Build completed successfully"
49+
50+
- name: Check bundle size
51+
run: |
52+
echo "Running bundle size checker..."
53+
pnpm run size:check
54+
echo "Size check completed"
55+
56+
- name: Display dist contents
57+
run: |
58+
echo "Dist directory structure:"
59+
find dist -type f -exec ls -lh {} \; | awk '{print $5, $9}'
60+
echo ""
61+
echo "Total dist size:"
62+
du -sh dist

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ pnpm test:debug
126126

127127
**For Production Release:**
128128

129-
1. Run full validation: `pnpm validate` (lint + test + build)
129+
1. Run full validation: `pnpm validate` (lint + test + build + size check)
130130
2. Ensure coverage requirements are met with `pnpm test:coverage`
131131
3. Run security checks: `pnpm secure` (if Snyk is configured)
132132
4. Create PR to `main` for review

README.md

Lines changed: 234 additions & 37 deletions
Large diffs are not rendered by default.

eslint.config.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import tsPlugin from '@typescript-eslint/eslint-plugin';
22
import tsParser from '@typescript-eslint/parser';
3+
import securityPlugin from 'eslint-plugin-security';
34

45
export default [
56
{
@@ -15,6 +16,7 @@ export default [
1516
},
1617
plugins: {
1718
'@typescript-eslint': tsPlugin,
19+
'security': securityPlugin,
1820
},
1921
rules: {
2022
// TypeScript specific rules
@@ -42,6 +44,22 @@ export default [
4244
'no-implied-eval': 'error',
4345
'no-new-func': 'error',
4446
'no-script-url': 'error',
47+
48+
// Security plugin rules - disabled here (run separately in lint:security)
49+
// but included so eslint-disable comments are recognized
50+
'security/detect-object-injection': 'off',
51+
'security/detect-non-literal-regexp': 'off',
52+
'security/detect-unsafe-regex': 'off',
53+
'security/detect-buffer-noassert': 'off',
54+
'security/detect-child-process': 'off',
55+
'security/detect-disable-mustache-escape': 'off',
56+
'security/detect-eval-with-expression': 'off',
57+
'security/detect-no-csrf-before-method-override': 'off',
58+
'security/detect-non-literal-fs-filename': 'off',
59+
'security/detect-non-literal-require': 'off',
60+
'security/detect-possible-timing-attacks': 'off',
61+
'security/detect-pseudoRandomBytes': 'off',
62+
'security/detect-bidi-characters': 'off',
4563
},
4664
},
4765
{
@@ -56,6 +74,7 @@ export default [
5674
},
5775
plugins: {
5876
'@typescript-eslint': tsPlugin,
77+
'security': securityPlugin,
5978
},
6079
rules: {
6180
// Relax some rules for test files

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@wgtechlabs/log-engine",
3-
"version": "2.2.2",
3+
"version": "2.3.0",
44
"description": "A lightweight, security-first logging utility with automatic data redaction for Node.js applications - the first logging library with built-in PII protection.",
55
"type": "module",
66
"keywords": [
@@ -103,7 +103,8 @@
103103
"secure:test": "snyk test --org=wgtechlabs",
104104
"secure:code": "snyk code test --org=wgtechlabs",
105105
"secure": "run-s lint:security secure:test secure:code",
106-
"validate": "run-s lint test build",
106+
"size:check": "node scripts/check-size.js",
107+
"validate": "run-s lint test build size:check",
107108
"clean": "rm -rf dist coverage",
108109
"coverage:open": "open coverage/lcov-report/index.html",
109110
"coverage:upload": "npx codecov"

scripts/check-size.js

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* @wgtechlabs/log-engine - Bundle Size Checker
5+
*
6+
* This script validates that the built package remains lightweight by checking
7+
* the total size of the dist directory and individual build formats.
8+
*
9+
* ## Purpose
10+
* As a lightweight logging library with zero dependencies, it's critical to
11+
* maintain a small bundle size. This checker ensures the package doesn't
12+
* exceed reasonable size limits for a lightweight library.
13+
*
14+
* ## Size Limits
15+
* - Total dist size: 1MB (1024KB)
16+
* - ESM build: 512KB
17+
* - CJS build: 512KB
18+
*
19+
* ## Usage
20+
* ```bash
21+
* node scripts/check-size.js
22+
* pnpm run size:check
23+
* ```
24+
*
25+
* ## Exit Codes
26+
* - 0: All size checks passed
27+
* - 1: Size limit exceeded or dist directory not found
28+
*
29+
* @author WG Tech Labs
30+
* @license MIT
31+
*/
32+
33+
import { stat, readdir } from 'fs/promises';
34+
import { join, dirname } from 'path';
35+
import { fileURLToPath } from 'url';
36+
import { existsSync } from 'fs';
37+
38+
const __filename = fileURLToPath(import.meta.url);
39+
const __dirname = dirname(__filename);
40+
41+
// Size limits in KB
42+
const SIZE_LIMITS = {
43+
total: 1024, // 1MB total dist size
44+
esm: 512, // 512KB for ESM build
45+
cjs: 512, // 512KB for CJS build
46+
};
47+
48+
/**
49+
* Calculate the total size of a directory recursively
50+
*
51+
* @param {string} dirPath - Path to directory
52+
* @returns {Promise<number>} Total size in bytes
53+
*/
54+
async function getDirectorySize(dirPath) {
55+
let totalSize = 0;
56+
57+
try {
58+
const entries = await readdir(dirPath, { withFileTypes: true });
59+
60+
for (const entry of entries) {
61+
const fullPath = join(dirPath, entry.name);
62+
63+
if (entry.isDirectory()) {
64+
totalSize += await getDirectorySize(fullPath);
65+
} else if (entry.isFile()) {
66+
const stats = await stat(fullPath);
67+
totalSize += stats.size;
68+
}
69+
}
70+
} catch (error) {
71+
// Only treat missing directories as empty; surface other errors
72+
if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') {
73+
return 0;
74+
}
75+
76+
console.error(
77+
`Failed to calculate directory size for "${dirPath}":`,
78+
error
79+
);
80+
throw error;
81+
}
82+
83+
return totalSize;
84+
}
85+
86+
/**
87+
* Format bytes to human-readable string
88+
*
89+
* @param {number} bytes - Size in bytes
90+
* @returns {string} Formatted size string
91+
*/
92+
function formatBytes(bytes) {
93+
if (bytes === 0) return '0 B';
94+
95+
const kb = bytes / 1024;
96+
if (kb < 1) return `${bytes} B`;
97+
98+
const mb = kb / 1024;
99+
if (mb < 1) return `${kb.toFixed(2)} KB`;
100+
101+
return `${mb.toFixed(2)} MB`;
102+
}
103+
104+
/**
105+
* Check if size is within limit
106+
*
107+
* @param {number} sizeBytes - Actual size in bytes
108+
* @param {number} limitKB - Limit in KB
109+
* @param {string} label - Label for the check
110+
* @returns {boolean} True if within limit
111+
*/
112+
function checkSize(sizeBytes, limitKB, label) {
113+
const limitBytes = limitKB * 1024;
114+
const percentage = (sizeBytes / limitBytes) * 100;
115+
116+
const passed = sizeBytes <= limitBytes;
117+
const icon = passed ? '✅' : '❌';
118+
const status = passed ? 'PASS' : 'FAIL';
119+
120+
console.log(`${icon} ${label.padEnd(20)} ${formatBytes(sizeBytes).padEnd(12)} / ${formatBytes(limitBytes).padEnd(12)} (${percentage.toFixed(1)}%) [${status}]`);
121+
122+
return passed;
123+
}
124+
125+
/**
126+
* Main size checker function
127+
*/
128+
async function main() {
129+
console.log('\n🔍 Log Engine Bundle Size Checker\n');
130+
console.log('═'.repeat(80));
131+
console.log();
132+
133+
const projectRoot = join(__dirname, '..');
134+
const distPath = join(projectRoot, 'dist');
135+
136+
// Check if dist directory exists
137+
if (!existsSync(distPath)) {
138+
console.error('❌ Error: dist directory not found. Please run "pnpm build" first.\n');
139+
process.exit(1);
140+
}
141+
142+
const esmPath = join(distPath, 'esm');
143+
const cjsPath = join(distPath, 'cjs');
144+
145+
// Check if expected build outputs exist
146+
if (!existsSync(esmPath)) {
147+
console.error('❌ Error: dist/esm directory not found. Build may be incomplete.\n');
148+
process.exit(1);
149+
}
150+
151+
if (!existsSync(cjsPath)) {
152+
console.error('❌ Error: dist/cjs directory not found. Build may be incomplete.\n');
153+
process.exit(1);
154+
}
155+
156+
// Get directory sizes
157+
const totalSize = await getDirectorySize(distPath);
158+
const esmSize = await getDirectorySize(esmPath);
159+
const cjsSize = await getDirectorySize(cjsPath);
160+
161+
console.log('📊 Bundle Size Report:\n');
162+
163+
// Check sizes against limits
164+
const results = [];
165+
results.push(checkSize(totalSize, SIZE_LIMITS.total, 'Total (dist)'));
166+
results.push(checkSize(esmSize, SIZE_LIMITS.esm, 'ESM Build'));
167+
results.push(checkSize(cjsSize, SIZE_LIMITS.cjs, 'CJS Build'));
168+
169+
console.log();
170+
console.log('═'.repeat(80));
171+
172+
// Determine overall result
173+
const allPassed = results.every(result => result === true);
174+
175+
if (allPassed) {
176+
console.log('\n✅ All size checks passed! Package remains lightweight.\n');
177+
process.exit(0);
178+
} else {
179+
console.log('\n❌ Size limit exceeded! Please review and optimize the bundle size.\n');
180+
console.log('💡 Tips to reduce bundle size:');
181+
console.log(' - Remove unused code and dependencies');
182+
console.log(' - Check for duplicate code that can be refactored');
183+
console.log(' - Review imports and ensure tree-shaking works properly');
184+
console.log(' - Consider code splitting for large features\n');
185+
process.exit(1);
186+
}
187+
}
188+
189+
// Run the checker
190+
main().catch((error) => {
191+
console.error('\n❌ Unexpected error:', error.message);
192+
console.error(error.stack);
193+
process.exit(1);
194+
});

0 commit comments

Comments
 (0)