| name | cwl-use-expressions |
| description | Use CWL expressions and JavaScript for dynamic behavior including parameter transformation,
conditional logic, and computed values. Learn to leverage InlineJavascriptRequirement for powerful
CWL packages. Use when you need dynamic, computed, or conditional behavior in CWL processes.
|
| license | Apache-2.0 |
| compatibility | Requires CWL v1.0+ with InlineJavascriptRequirement support. |
| metadata | {"author":"fmigneault"} |
Use CWL Expressions and JavaScript
Master CWL expressions and JavaScript for dynamic, powerful CWL packages.
When to Use
- Computing values from inputs
- Conditional execution or arguments
- Transforming file names or paths
- Dynamic output naming
- Complex parameter manipulation
- Conditional validation
Expression Syntax
Parameter References
$(inputs.parameter_name)
$(self)
$(runtime.outdir)
$(runtime.tmpdir)
$(runtime.cores)
$(runtime.ram)
Simple Expressions
inputs:
value:
type: int
inputBinding:
valueFrom: $(self * 2)
Enabling JavaScript
InlineJavascriptRequirement
requirements:
InlineJavascriptRequirement: {}
JavaScript Expressions
Basic Syntax
valueFrom: $(inputs.x + inputs.y)
valueFrom: |
${
return inputs.x + inputs.y;
}
String Manipulation
inputs:
filename:
type: string
outputs:
output:
type: File
outputBinding:
glob: |
${
return inputs.filename.replace('.txt', '_processed.txt');
}
File Operations
inputs:
input_file:
type: File
arguments:
- valueFrom: $(inputs.input_file.basename)
- valueFrom: $(inputs.input_file.nameroot)
- valueFrom: $(inputs.input_file.nameext)
- valueFrom: $(inputs.input_file.dirname)
- valueFrom: $(inputs.input_file.size)
Common Patterns
Conditional Arguments
requirements:
InlineJavascriptRequirement: {}
inputs:
verbose:
type: boolean
default: false
debug:
type: boolean?
arguments:
- valueFrom: |
${
return inputs.verbose ? "--verbose" : null;
}
- valueFrom: |
${
return inputs.debug ? "--debug" : null;
}
Computed Output Names
inputs:
input_file:
type: File
prefix:
type: string
default: "processed"
outputs:
output_file:
type: File
outputBinding:
glob: |
${
var base = inputs.input_file.nameroot;
var ext = inputs.input_file.nameext;
return inputs.prefix + "_" + base + ext;
}
Array Processing
inputs:
files:
type: File[]
arguments:
- valueFrom: |
${
return inputs.files.map(function(f) {
return f.path;
}).join(',');
}
Conditional Defaults
inputs:
threshold:
type: float?
auto_threshold:
type: boolean
default: false
arguments:
- prefix: --threshold
valueFrom: |
${
if (inputs.threshold !== null) {
return inputs.threshold;
} else if (inputs.auto_threshold) {
return 0.5; // Auto value
} else {
return null; // No threshold
}
}
Advanced Techniques
Complex Validation
requirements:
InlineJavascriptRequirement: {}
inputs:
value:
type: int
arguments:
- valueFrom: |
${
if (inputs.value < 0 || inputs.value > 100) {
throw "Value must be between 0 and 100";
}
return inputs.value;
}
Dynamic Command Building
baseCommand: [python, -c]
inputs:
operation:
type: string
value_a:
type: float
value_b:
type: float
arguments:
- valueFrom: |
${
var ops = {
"add": inputs.value_a + inputs.value_b,
"subtract": inputs.value_a - inputs.value_b,
"multiply": inputs.value_a * inputs.value_b,
"divide": inputs.value_a / inputs.value_b
};
return "print(" + ops[inputs.operation] + ")";
}
Format Conversion
inputs:
date_string:
type: string
arguments:
- valueFrom: |
${
// Convert date format
var parts = inputs.date_string.split('-');
return parts[2] + '/' + parts[1] + '/' + parts[0];
// Returns: "19/02/2026"
}
Resource Calculation
requirements:
ResourceRequirement:
ramMin: |
${
// Calculate RAM based on input file size
var fileSize = inputs.input_file.size / (1024 * 1024); // MB
return Math.max(2048, fileSize * 4); // 4x file size, min 2GB
}
Array Filtering
inputs:
files:
type: File[]
min_size:
type: int
default: 0
arguments:
- valueFrom: |
${
// Filter files by size
return inputs.files
.filter(function(f) {
return f.size > inputs.min_size;
})
.map(function(f) {
return f.path;
})
.join(' ');
}
InitialWorkDirRequirement with Expressions
Dynamic File Generation
requirements:
InlineJavascriptRequirement: {}
InitialWorkDirRequirement:
listing:
- entryname: config.json
entry: |
${
return JSON.stringify({
"input": inputs.input_file.path,
"threshold": inputs.threshold,
"output": runtime.outdir + "/result.txt"
}, null, 2);
}
Conditional File Staging
requirements:
InitialWorkDirRequirement:
listing: |
${
var files = [inputs.required_file];
if (inputs.optional_file !== null) {
files.push(inputs.optional_file);
}
return files;
}
Runtime Information
Available Runtime Properties
arguments:
- valueFrom: $(runtime.outdir)
- valueFrom: $(runtime.tmpdir)
- valueFrom: $(runtime.cores)
- valueFrom: $(runtime.ram)
Using Runtime in Paths
outputs:
output:
type: File
outputBinding:
glob: |
${
return runtime.outdir + "/output.txt";
}
Debugging Expressions
Add Logging
arguments:
- valueFrom: |
${
console.log("Input value:", inputs.value);
console.log("Computed result:", inputs.value * 2);
return inputs.value * 2;
}
Test Locally
cwltool --debug tool.cwl inputs.json
Best Practices
1. Keep Expressions Simple
valueFrom: |
${
var result;
if (inputs.a) {
if (inputs.b) {
result = inputs.a + inputs.b;
} else {
result = inputs.a;
}
} else {
result = 0;
}
return result;
}
valueFrom: $(inputs.a + (inputs.b || 0))
2. Use Null Checks
valueFrom: |
${
return inputs.optional !== null ? inputs.optional : "default";
}
3. Document Complex Expressions
inputs:
files:
type: File[]
arguments:
- valueFrom: |
${
return inputs.files
.filter(function(f) { return f.size > 1048576; })
.map(function(f) { return f.path; })
.join(',');
}
4. Avoid Side Effects
valueFrom: |
${
inputs.value = inputs.value * 2; // Bad!
return inputs.value;
}
valueFrom: $(inputs.value * 2)
5. Handle Errors Gracefully
valueFrom: |
${
try {
return someComplexOperation(inputs.value);
} catch (e) {
console.error("Error:", e.message);
throw e;
}
}
Common Gotchas
Null vs Undefined
valueFrom: |
${
// ✅ Check for null
if (inputs.optional === null) {
return "default";
}
return inputs.optional;
}
File Path vs Object
The inputs.file is an object with properties. The file portion is the input ID.
valueFrom: $(inputs.file.path)
valueFrom: $(inputs.file)
String Concatenation
valueFrom: $(inputs.prefix + "_" + inputs.suffix)
valueFrom: $(inputs.prefix inputs.suffix)
Related Skills
Documentation
Examples Repository
Check the CWL examples repository for more expression patterns:
https://github.com/common-workflow-language/workflows