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
56 changes: 56 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,62 @@ Returns `{ code, map }`. `map` will be undefined if no sourcemap was supplied.
- `moduleType` - The type of module being transformed.
- `sourcemap` - Optional existing source map for the code.

## CLI Tool

The package includes a CLI tool for applying transformations to source files:

```bash
npx @apm-js-collab/code-transformer transformer.js source-file.js
```

### CLI Usage

The CLI tool takes two arguments:
1. `transformer.js` - A file that exports instrumentation configuration(s)
2. `source-file.js` - The source file to transform (can be any path, including node_modules)

The transformed code is written to stdout, which you can redirect to a file or pipe to other commands.

### CLI Example

Create a transformer configuration file:

```javascript
// my-transformer.js
module.exports = [{
channelName: 'my-fetch-channel',
module: {
name: 'my-module',
versionRange: '>=1.0.0',
filePath: 'index.js'
},
functionQuery: {
functionName: 'fetch',
kind: 'Async'
}
}]
```

Apply the transformation:

```bash
npx @apm-js-collab/code-transformer my-transformer.js lib/index.js > instrumented.js
```

The transformer configuration file can also export an object with additional options:

```javascript
module.exports = {
configs: [/* array of configs */],
dcModule: './custom-diagnostics-channel.js', // optional custom dc module
customTransforms: { // optional custom transform functions
myCustomTransform: (state, node) => {
// custom AST transformation logic
}
}
}
```

## License

See LICENSE
118 changes: 118 additions & 0 deletions cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#!/usr/bin/env node
'use strict'

const { readFileSync } = require('fs')
const { resolve, basename } = require('path')
const { Transformer } = require('./lib/transformer.js')

function printUsage () {
console.error(`Usage: npx @apm-js-collab/code-transformer <transformer.js> <source-file>

Arguments:
transformer.js Path to transformer configuration file
source-file Path to source file to transform (can be a node module path)

The transformer.js file should export one of:
- An array of instrumentation configs
- An object with { configs, dcModule?, customTransforms? }

Example transformer.js:
module.exports = [{
channelName: 'my-channel',
module: { name: 'my-module', versionRange: '>=1.0.0', filePath: 'index.js' },
functionQuery: { functionName: 'myFunction', kind: 'Async' }
}]

The transformed code will be written to stdout.
`)
process.exit(1)
}

function main () {
const args = process.argv.slice(2)

if (args.length !== 2) {
printUsage()
}

const [transformerPath, sourceFilePath] = args

// Resolve paths
const absoluteTransformerPath = resolve(process.cwd(), transformerPath)
const absoluteSourcePath = resolve(process.cwd(), sourceFilePath)

// Load transformer configuration
let transformerConfig
try {
transformerConfig = require(absoluteTransformerPath)
} catch (error) {
console.error(`Error loading transformer file: ${error.message}`)
process.exit(1)
}

// Parse transformer config
let configs
let dcModule
let customTransforms = {}

if (Array.isArray(transformerConfig)) {
configs = transformerConfig
} else if (typeof transformerConfig === 'object' && transformerConfig.configs) {
configs = transformerConfig.configs
dcModule = transformerConfig.dcModule
customTransforms = transformerConfig.customTransforms || {}
} else {
console.error('Transformer file must export an array of configs or an object with { configs, dcModule?, customTransforms? }')
process.exit(1)
}

if (!Array.isArray(configs) || configs.length === 0) {
console.error('Transformer must provide at least one config')
process.exit(1)
}

// Read source file
let sourceCode
try {
sourceCode = readFileSync(absoluteSourcePath, 'utf-8')
} catch (error) {
console.error(`Error reading source file: ${error.message}`)
process.exit(1)
}

// Determine module type from file extension
const moduleType = absoluteSourcePath.endsWith('.mjs') || absoluteSourcePath.endsWith('.mts')
? 'esm'
: absoluteSourcePath.endsWith('.cjs') || absoluteSourcePath.endsWith('.cts')
? 'cjs'
: 'unknown'

// Extract module information from first config or use defaults
const firstConfig = configs[0]
const moduleName = firstConfig.module?.name || 'cli-module'
const version = '1.0.0'
const filePath = basename(absoluteSourcePath)

// Create transformer and apply transformations
try {
// Create transformer directly with the configs
const transformer = new Transformer(
moduleName,
version,
filePath,
configs,
dcModule || 'diagnostics_channel',
customTransforms
)

const result = transformer.transform(sourceCode, moduleType)

// Output transformed code to stdout
process.stdout.write(result.code)
} catch (error) {
console.error(`Error transforming code: ${error.message}`)
process.exit(1)
}
}

main()
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@
"files": [
"./index.js",
"./index.d.ts",
"./cli.js",
"./lib/**/*.js",
"LICENSE",
"NOTICE"
],
"bin": {
"code-transformer": "./cli.js"
},
"main": "./index.js",
"types": "./index.d.ts",
"scripts": {
Expand Down
Loading