Skip to content
Open
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
59 changes: 59 additions & 0 deletions bundle-tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Twilio Node.js Bundle Tests

This directory contains tests to verify that the Twilio Node.js package can be successfully bundled with various bundlers.

## Bundlers Tested

- **esbuild**: Fast JavaScript bundler
- **Vite**: Modern frontend build tool based on Rollup
- **Webpack**: Widely used module bundler
- **SWC**: Rust-based transpiler (similar to Babel)
- **Parcel**: Zero-config bundler

## Setup

Install dependencies for the bundle tests:

```bash
cd bundle-tests
npm install
```

## Running Tests

You can run all bundle tests:

```bash
npm test
```

Or run tests for a specific bundler:

```bash
npm run test:esbuild
npm run test:vite
npm run test:webpack
npm run test:swc
npm run test:parcel
```

From the root directory, you can also run:

```bash
npm run test:bundle # Run all bundle tests
npm run test:bundle:esbuild # Run esbuild test only
# etc.
```

## How It Works

Each test:
1. Takes a simple test file that imports the Twilio package
2. Attempts to bundle it with the specific bundler
3. Reports success or failure

The `run-all-tests.js` script runs each test sequentially and provides a summary of results.

## Integration with npm lifecycle

The bundle tests are automatically run as part of the `prepublish` script, ensuring that the package can be successfully bundled with all supported bundlers before publishing to npm.
38 changes: 38 additions & 0 deletions bundle-tests/bundle-dynamic-import-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Test file to verify dynamicImport function behavior in bundled environments
console.log('Testing dynamicImport function in bundled environment...');

// Create a function to test dynamic imports
const testDynamicImport = async () => {
// Define the dynamicImport function (same as in src/index.ts)
const dynamicImport = (path) => {
// This will work in Node.js but cause issues with some bundlers
return new Function('return import("' + path + '")')();
};

try {
console.log('Attempting dynamic import of fs module...');
const fs = await dynamicImport('fs');

if (fs && typeof fs.readFileSync === 'function') {
console.log('✅ Dynamic import successful in this bundler!');
return true;
} else {
console.log('⚠️ Dynamic import returned unexpected results:');
console.log(fs);
return false;
}
} catch (error) {
console.error('❌ Dynamic import failed:', error);
console.log('This is expected behavior in certain bundlers as dynamicImport uses new Function() and dynamic imports which may not be supported');
return false;
}
};

// Run the test
testDynamicImport().then(success => {
if (success) {
console.log('Test completed successfully - dynamic imports work in this environment');
} else {
console.log('Test completed - dynamic imports are not supported in this bundled environment');
}
});
34 changes: 34 additions & 0 deletions bundle-tests/dynamic-import-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Test file to specifically test dynamicImport functionality
console.log('Testing dynamicImport function...');

// Create our own implementation of the dynamicImport function for testing
// This is the same implementation from src/index.ts
const dynamicImport = (path) => {
// This will work in Node.js but cause issues with some bundlers
return new Function('return import("' + path + '")')();
};

console.log('Running dynamicImport function with "fs" module...');

// Test with fs module since that's used in the original example
dynamicImport('fs').then(fs => {
if (fs && typeof fs.readFileSync === 'function') {
console.log('✅ Dynamic import successful!');
console.log('fs.readFileSync exists:', typeof fs.readFileSync === 'function');

// Try using readFileSync to prove it works
try {
const packageJson = fs.readFileSync('../package.json', 'utf8');
const parsed = JSON.parse(packageJson);
console.log('Package name:', parsed.name);
console.log('Package version:', parsed.version);
} catch (e) {
console.error('Error reading package.json:', e);
}
} else {
console.log('⚠️ Dynamic import returned unexpected results');
console.log('fs module:', fs);
}
}).catch(e => {
console.error('❌ Dynamic import failed:', e);
});
74 changes: 74 additions & 0 deletions bundle-tests/esbuild-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
const esbuild = require('esbuild');
const path = require('path');
const fs = require('fs');
const { exec } = require('child_process');

async function bundleWithEsbuild() {
try {
// Create output directory if it doesn't exist
const outdir = path.join(__dirname, 'esbuild-output');
if (!fs.existsSync(outdir)) {
fs.mkdirSync(outdir, { recursive: true });
}

// Build standard test
console.log('\n--- Building standard test bundle ---\n');
const standardResult = await esbuild.build({
entryPoints: [path.join(__dirname, 'test.js')],
bundle: true,
platform: 'node',
target: 'node14', // Match the package.json "engines"
outfile: path.join(outdir, 'bundle.js'),
external: ['crypto', 'fs', 'https', 'http', 'os', 'path', 'util', 'querystring', 'url', 'stream', 'events', 'net', 'tls', 'zlib', 'buffer'],
logLevel: 'info',
metafile: true,
});

// Build dynamicImport test bundle
console.log('\n--- Building dynamicImport test bundle ---\n');
const dynamicResult = await esbuild.build({
entryPoints: [path.join(__dirname, 'bundle-dynamic-import-test.js')],
bundle: true,
platform: 'node',
target: 'node14',
outfile: path.join(outdir, 'dynamic-bundle.js'),
external: ['crypto', 'fs', 'https', 'http', 'os', 'path', 'util', 'querystring', 'url', 'stream', 'events', 'net', 'tls', 'zlib', 'buffer'],
logLevel: 'info',
metafile: true,
});

// Log standard metafile info
console.log('\n--- Standard bundle analysis ---\n');
const standardText = await esbuild.analyzeMetafile(standardResult.metafile);
console.log(standardText);

// Log dynamicImport metafile info
console.log('\n--- DynamicImport bundle analysis ---\n');
const dynamicText = await esbuild.analyzeMetafile(dynamicResult.metafile);
console.log(dynamicText);

console.log('\n--- Running dynamicImport test bundle ---\n');
return new Promise((resolve) => {
exec(`node ${path.join(outdir, 'dynamic-bundle.js')}`, (error, stdout, stderr) => {
if (error) {
console.error(`Error running dynamic import test: ${error.message}`);
return resolve(1);
}
if (stderr) {
console.error(`stderr: ${stderr}`);
}
console.log(stdout);
console.log('esbuild bundle tests completed successfully');
resolve(0);
});
});
} catch (error) {
console.error('esbuild bundle failed:', error);
return 1;
}
}

// Run the bundle test
bundleWithEsbuild().then(exitCode => {
process.exit(exitCode);
});
35 changes: 35 additions & 0 deletions bundle-tests/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "twilio-bundle-tests",
"version": "1.0.0",
"private": true,
"description": "Bundle tests for twilio-node package",
"dependencies": {
"twilio": "file:.."
},
"devDependencies": {
"@babel/core": "^7.23.0",
"@babel/preset-env": "^7.22.20",
"@parcel/config-default": "^2.9.3",
"@parcel/core": "^2.9.3",
"@parcel/optimizer-terser": "^2.9.3",
"@parcel/transformer-js": "^2.9.3",
"@swc/core": "^1.3.82",
"babel-loader": "^9.1.3",
"esbuild": "^0.19.3",
"parcel": "^2.9.3",
"vite": "^4.4.9",
"webpack": "^5.88.2",
"webpack-cli": "^5.1.4"
},
"engines": {
"node": ">=14.0"
},
"scripts": {
"test": "node run-all-tests.js",
"test:esbuild": "node esbuild-test.js",
"test:vite": "node vite-test.js",
"test:webpack": "node webpack-test.js",
"test:swc": "node swc-test.js",
"test:parcel": "node parcel-test.js"
}
}
108 changes: 108 additions & 0 deletions bundle-tests/parcel-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
const { Parcel } = require('@parcel/core');
const path = require('path');
const fs = require('fs');

async function bundleWithParcel() {
try {
// Create output directory if it doesn't exist
const outdir = path.join(__dirname, 'parcel-output');
if (!fs.existsSync(outdir)) {
fs.mkdirSync(outdir, { recursive: true });
}

// Create .parcelrc file for Node.js targets
const parcelConfigPath = path.join(__dirname, '.parcelrc');
fs.writeFileSync(
parcelConfigPath,
JSON.stringify({
"extends": "@parcel/config-default",
"transformers": {
"*.{js,mjs,jsx,cjs,ts,tsx}": ["@parcel/transformer-js"]
},
"optimizers": {
"*.js": ["@parcel/optimizer-terser"]
}
}, null, 2)
);

// Create a temporary package.json in the bundle-tests directory
// to ensure Parcel can resolve dependencies correctly
const packageJsonPath = path.join(__dirname, 'package.json');
if (!fs.existsSync(packageJsonPath)) {
fs.writeFileSync(
packageJsonPath,
JSON.stringify({
"name": "twilio-bundle-test",
"private": true,
"dependencies": {
"twilio": "file:.."
},
"targets": {
"node": {
"engines": {
"node": ">= 14"
}
}
}
}, null, 2)
);
}

// Initialize bundler
const bundler = new Parcel({
entries: path.join(__dirname, 'test.js'),
defaultConfig: '@parcel/config-default',
targets: {
main: {
distDir: outdir,
engines: {
node: '>=14'
}
}
},
defaultTargetOptions: {
distDir: outdir,
engines: {
node: '>=14'
},
outputFormat: 'commonjs',
isLibrary: true,
optimize: true,
},
mode: 'production',
shouldDisableCache: true
});

// Run the bundler
const { bundleGraph, buildTime } = await bundler.run();

const bundles = bundleGraph.getBundles();
console.log(`Parcel bundled ${bundles.length} bundle(s) in ${buildTime}ms`);
bundles.forEach(bundle => {
console.log(`- ${bundle.filePath} (${bundle.type}): ${prettyBytes(bundle.stats.size)}`);
});

console.log('Parcel bundle completed successfully');

// Clean up config
fs.unlinkSync(parcelConfigPath);

return 0;
} catch (error) {
console.error('Parcel bundle failed:', error);
return 1;
}
}

// Helper function to format bytes
function prettyBytes(bytes) {
const units = ['B', 'KB', 'MB', 'GB'];
const exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
const size = bytes / Math.pow(1024, exponent);
return `${size.toFixed(2)} ${units[exponent]}`;
}

// Run the bundle test
bundleWithParcel().then(exitCode => {
process.exit(exitCode);
});
Loading
Loading