ts2workflows 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +82 -0
- package/dist/ast/expressions.d.ts +57 -0
- package/dist/ast/expressions.d.ts.map +1 -0
- package/dist/ast/expressions.js +300 -0
- package/dist/ast/stepnames.d.ts +9 -0
- package/dist/ast/stepnames.d.ts.map +1 -0
- package/dist/ast/stepnames.js +268 -0
- package/dist/ast/steps.d.ts +176 -0
- package/dist/ast/steps.d.ts.map +1 -0
- package/dist/ast/steps.js +534 -0
- package/dist/ast/validation.d.ts +20 -0
- package/dist/ast/validation.d.ts.map +1 -0
- package/dist/ast/validation.js +214 -0
- package/dist/ast/workflows.d.ts +28 -0
- package/dist/ast/workflows.d.ts.map +1 -0
- package/dist/ast/workflows.js +74 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +114 -0
- package/dist/errors.d.ts +18 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +12 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/transpiler/asserts.d.ts +7 -0
- package/dist/transpiler/asserts.d.ts.map +1 -0
- package/dist/transpiler/asserts.js +11 -0
- package/dist/transpiler/expressions.d.ts +6 -0
- package/dist/transpiler/expressions.d.ts.map +1 -0
- package/dist/transpiler/expressions.js +223 -0
- package/dist/transpiler/generated/functionMetadata.d.ts +2 -0
- package/dist/transpiler/generated/functionMetadata.d.ts.map +1 -0
- package/dist/transpiler/generated/functionMetadata.js +324 -0
- package/dist/transpiler/index.d.ts +2 -0
- package/dist/transpiler/index.d.ts.map +1 -0
- package/dist/transpiler/index.js +74 -0
- package/dist/transpiler/statements.d.ts +7 -0
- package/dist/transpiler/statements.d.ts.map +1 -0
- package/dist/transpiler/statements.js +533 -0
- package/dist/transpiler/transformations.d.ts +28 -0
- package/dist/transpiler/transformations.d.ts.map +1 -0
- package/dist/transpiler/transformations.js +461 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +3 -0
- package/language_reference.md +771 -0
- package/package.json +62 -0
- package/types/workflowslib.d.ts +714 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
export class WorkflowValidationError extends Error {
|
|
2
|
+
issues;
|
|
3
|
+
constructor(issues) {
|
|
4
|
+
const issueTypes = Array.from(new Set(issues.map((x) => x.type))).join(', ');
|
|
5
|
+
super(`Workflow validation error: ${issueTypes}`);
|
|
6
|
+
this.name = this.constructor.name;
|
|
7
|
+
this.issues = issues;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
const validators = new Map([
|
|
11
|
+
['invalidWorkflowName', validateWorkflowNames],
|
|
12
|
+
['duplicatedStepName', validateNoDuplicateStepNames],
|
|
13
|
+
['duplicatedSubworkflowName', validateNoDuplicateSubworkflowNames],
|
|
14
|
+
['missingJumpTarget', validateJumpTargets],
|
|
15
|
+
['wrongNumberOfCallArguments', validateSubworkflowArguments],
|
|
16
|
+
]);
|
|
17
|
+
/**
|
|
18
|
+
* Execute all syntax validators on a WorkflowApp app.
|
|
19
|
+
*
|
|
20
|
+
* Throws a WorkflowValidationError if there are errors.
|
|
21
|
+
*/
|
|
22
|
+
export function validate(app, disabled = []) {
|
|
23
|
+
const selectedValidators = Array.from(validators.entries()).filter(([name]) => {
|
|
24
|
+
return !disabled.includes(name);
|
|
25
|
+
});
|
|
26
|
+
const issues = [];
|
|
27
|
+
for (const [, validator] of selectedValidators) {
|
|
28
|
+
issues.push(...validator(app));
|
|
29
|
+
}
|
|
30
|
+
if (issues.length > 0) {
|
|
31
|
+
throw new WorkflowValidationError(issues);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Returns all validator names.
|
|
36
|
+
*/
|
|
37
|
+
export function validatorNames() {
|
|
38
|
+
return Array.from(validators.keys());
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Check that workflow does not contain duplicated step names.
|
|
42
|
+
*/
|
|
43
|
+
function validateNoDuplicateStepNames(app) {
|
|
44
|
+
function collectDuplicateStepName(wf) {
|
|
45
|
+
const seen = new Set();
|
|
46
|
+
const duplicates = new Set();
|
|
47
|
+
for (const { name } of wf.iterateStepsDepthFirst()) {
|
|
48
|
+
if (seen.has(name)) {
|
|
49
|
+
duplicates.add(name);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
seen.add(name);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return Array.from(duplicates.values());
|
|
56
|
+
}
|
|
57
|
+
const issues = [];
|
|
58
|
+
for (const subworkflow of app.subworkflows) {
|
|
59
|
+
const duplicatesInSub = collectDuplicateStepName(subworkflow);
|
|
60
|
+
if (duplicatesInSub.length > 0) {
|
|
61
|
+
const message = `Duplicated step names in the subworkflow ${subworkflow.name}: ${duplicatesInSub.join(', ')}`;
|
|
62
|
+
issues.push({ type: 'duplicatedStepName', message: message });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return issues;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Check that there are no two subworkflows sharing a name.
|
|
69
|
+
*/
|
|
70
|
+
function validateNoDuplicateSubworkflowNames(app) {
|
|
71
|
+
const seen = new Set();
|
|
72
|
+
const duplicates = new Set();
|
|
73
|
+
const names = app.subworkflows.map((w) => w.name);
|
|
74
|
+
for (const name of names) {
|
|
75
|
+
if (seen.has(name)) {
|
|
76
|
+
duplicates.add(name);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
seen.add(name);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (duplicates.size > 0) {
|
|
83
|
+
const dup = Array.from(duplicates);
|
|
84
|
+
return [
|
|
85
|
+
{
|
|
86
|
+
type: 'duplicatedSubworkflowName',
|
|
87
|
+
message: `Duplicated subworkflow names: ${dup.join(', ')}`,
|
|
88
|
+
},
|
|
89
|
+
];
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Check that the subworkflow names are valid.
|
|
97
|
+
*/
|
|
98
|
+
function validateWorkflowNames(app) {
|
|
99
|
+
const issues = [];
|
|
100
|
+
const names = app.subworkflows.map((w) => w.name);
|
|
101
|
+
if (names.some((x) => x === '')) {
|
|
102
|
+
issues.push({
|
|
103
|
+
type: 'invalidWorkflowName',
|
|
104
|
+
message: 'Subworkflow must have a non-empty name',
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return issues;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Check that there are no jumps (calls, nexts) to non-existing steps or subworkflows
|
|
111
|
+
*/
|
|
112
|
+
function validateJumpTargets(app) {
|
|
113
|
+
const subworkflowNames = app.subworkflows.map((w) => w.name);
|
|
114
|
+
return app.subworkflows.flatMap((subworkflow) => {
|
|
115
|
+
return validateJumpTargetsInWorkflow(subworkflow, subworkflowNames);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
function validateJumpTargetsInWorkflow(workflow, subworkflowNames) {
|
|
119
|
+
const issues = [];
|
|
120
|
+
const stepNames = [];
|
|
121
|
+
for (const { name } of workflow.iterateStepsDepthFirst()) {
|
|
122
|
+
stepNames.push(name);
|
|
123
|
+
}
|
|
124
|
+
function validCallTarget(name) {
|
|
125
|
+
return (isRuntimeFunction(name) ||
|
|
126
|
+
stepNames.includes(name) ||
|
|
127
|
+
subworkflowNames.includes(name));
|
|
128
|
+
}
|
|
129
|
+
function validNextTarget(name) {
|
|
130
|
+
return stepNames.includes(name) || name === 'end'; // accepts "next: end"
|
|
131
|
+
}
|
|
132
|
+
for (const { name, step } of workflow.iterateStepsDepthFirst()) {
|
|
133
|
+
if (step.tag === 'call') {
|
|
134
|
+
if (!validCallTarget(step.call))
|
|
135
|
+
issues.push({
|
|
136
|
+
type: 'missingJumpTarget',
|
|
137
|
+
message: `Call target "${step.call}" in step "${name}" not found`,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
else if (step.tag === 'switch') {
|
|
141
|
+
if (step.next && !validNextTarget(step.next)) {
|
|
142
|
+
issues.push({
|
|
143
|
+
type: 'missingJumpTarget',
|
|
144
|
+
message: `Next target "${step.next}" in step "${name}" not found`,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
step.conditions.forEach((cond) => {
|
|
148
|
+
if (cond.next && !validNextTarget(cond.next)) {
|
|
149
|
+
issues.push({
|
|
150
|
+
type: 'missingJumpTarget',
|
|
151
|
+
message: `Next target "${cond.next}" in step "${name}" not found`,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return issues;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Check that call steps provide a correct number of argument in subworkflow calls
|
|
161
|
+
*/
|
|
162
|
+
function validateSubworkflowArguments(app) {
|
|
163
|
+
const issues = [];
|
|
164
|
+
const paramsBySubworkflow = new Map(app.subworkflows.map((x) => [
|
|
165
|
+
x.name,
|
|
166
|
+
{
|
|
167
|
+
required: x.params
|
|
168
|
+
?.filter((x) => typeof x.default === 'undefined')
|
|
169
|
+
.map((x) => x.name) ?? [],
|
|
170
|
+
optional: x.params
|
|
171
|
+
?.filter((x) => typeof x.default !== 'undefined')
|
|
172
|
+
.map((x) => x.name) ?? [],
|
|
173
|
+
},
|
|
174
|
+
]));
|
|
175
|
+
app.subworkflows.forEach((subw) => {
|
|
176
|
+
issues.push(...findIssuesInCallArguments(subw, paramsBySubworkflow));
|
|
177
|
+
});
|
|
178
|
+
return issues;
|
|
179
|
+
}
|
|
180
|
+
function findIssuesInCallArguments(wf, argumentBySubworkflowName) {
|
|
181
|
+
const issues = [];
|
|
182
|
+
for (const { name, step } of wf.iterateStepsDepthFirst()) {
|
|
183
|
+
if (step.tag === 'call' && argumentBySubworkflowName.has(step.call)) {
|
|
184
|
+
const requiredArgs = argumentBySubworkflowName.get(step.call)?.required ?? [];
|
|
185
|
+
const optionalArgs = argumentBySubworkflowName.get(step.call)?.optional ?? [];
|
|
186
|
+
const requiredAndOptionalArgs = requiredArgs.concat(optionalArgs);
|
|
187
|
+
const providedArgs = Object.keys(step.args ?? {});
|
|
188
|
+
const requiredButNotProvided = requiredArgs.filter((x) => !providedArgs.includes(x));
|
|
189
|
+
const providedButNotRequired = providedArgs.filter((x) => !requiredAndOptionalArgs.includes(x));
|
|
190
|
+
if (requiredButNotProvided.length > 0) {
|
|
191
|
+
issues.push({
|
|
192
|
+
type: 'wrongNumberOfCallArguments',
|
|
193
|
+
message: `Required parameters not provided on call step "${name}": ${JSON.stringify(requiredButNotProvided)}`,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
if (providedButNotRequired.length > 0) {
|
|
197
|
+
issues.push({
|
|
198
|
+
type: 'wrongNumberOfCallArguments',
|
|
199
|
+
message: `Extra arguments provided on call step "${name}": ${JSON.stringify(providedButNotRequired)}`,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return issues;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Returns true if functionName is a standard library or connector function.
|
|
208
|
+
*
|
|
209
|
+
* Current version does a minimalistic checking and assumes that a name is a
|
|
210
|
+
* standard library function if it contains a dot.
|
|
211
|
+
*/
|
|
212
|
+
function isRuntimeFunction(functionName) {
|
|
213
|
+
return functionName.includes('.');
|
|
214
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { VariableName, LiteralValueOrLiteralExpression } from './expressions.js';
|
|
2
|
+
import { NamedWorkflowStep } from './steps.js';
|
|
3
|
+
export interface WorkflowParameter {
|
|
4
|
+
name: VariableName;
|
|
5
|
+
default?: LiteralValueOrLiteralExpression;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* This is the main container class that brings together all subworkflows in a program
|
|
9
|
+
*/
|
|
10
|
+
export declare class WorkflowApp {
|
|
11
|
+
readonly subworkflows: Subworkflow[];
|
|
12
|
+
constructor(subworkflows?: Subworkflow[]);
|
|
13
|
+
render(): Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
export declare class Subworkflow {
|
|
16
|
+
readonly name: string;
|
|
17
|
+
readonly steps: NamedWorkflowStep[];
|
|
18
|
+
readonly params?: WorkflowParameter[];
|
|
19
|
+
constructor(name: string, steps: NamedWorkflowStep[], params?: WorkflowParameter[]);
|
|
20
|
+
render(): Record<string, unknown>;
|
|
21
|
+
renderBody(): Record<string, unknown>;
|
|
22
|
+
iterateStepsDepthFirst(): IterableIterator<NamedWorkflowStep>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Print the workflow as a YAML string.
|
|
26
|
+
*/
|
|
27
|
+
export declare function toYAMLString(workflow: WorkflowApp): string;
|
|
28
|
+
//# sourceMappingURL=workflows.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflows.d.ts","sourceRoot":"","sources":["../../src/ast/workflows.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,+BAA+B,EAAE,MAAM,kBAAkB,CAAA;AAChF,OAAO,EAAE,iBAAiB,EAA2B,MAAM,YAAY,CAAA;AAEvE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,YAAY,CAAA;IAClB,OAAO,CAAC,EAAE,+BAA+B,CAAA;CAC1C;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,QAAQ,CAAC,YAAY,EAAE,WAAW,EAAE,CAAA;gBAExB,YAAY,GAAE,WAAW,EAAO;IAI5C,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAKlC;AAGD,qBAAa,WAAW;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,iBAAiB,EAAE,CAAA;IACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,iBAAiB,EAAE,CAAA;gBAGnC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,iBAAiB,EAAE,EAC1B,MAAM,CAAC,EAAE,iBAAiB,EAAE;IAO9B,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAMjC,UAAU,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAuBpC,sBAAsB,IAAI,gBAAgB,CAAC,iBAAiB,CAAC;CAqB/D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,WAAW,GAAG,MAAM,CAI1D"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import * as YAML from 'yaml';
|
|
2
|
+
import { nestedSteps, renderStep } from './steps.js';
|
|
3
|
+
/**
|
|
4
|
+
* This is the main container class that brings together all subworkflows in a program
|
|
5
|
+
*/
|
|
6
|
+
export class WorkflowApp {
|
|
7
|
+
subworkflows;
|
|
8
|
+
constructor(subworkflows = []) {
|
|
9
|
+
this.subworkflows = subworkflows;
|
|
10
|
+
}
|
|
11
|
+
render() {
|
|
12
|
+
return Object.fromEntries(new Map(this.subworkflows.map((wf) => [wf.name, wf.renderBody()])));
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
// https://cloud.google.com/workflows/docs/reference/syntax/subworkflows
|
|
16
|
+
export class Subworkflow {
|
|
17
|
+
name;
|
|
18
|
+
steps;
|
|
19
|
+
params;
|
|
20
|
+
constructor(name, steps, params) {
|
|
21
|
+
this.name = name;
|
|
22
|
+
this.steps = steps;
|
|
23
|
+
this.params = params;
|
|
24
|
+
}
|
|
25
|
+
render() {
|
|
26
|
+
return {
|
|
27
|
+
[this.name]: this.renderBody(),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
renderBody() {
|
|
31
|
+
const body = {};
|
|
32
|
+
if (this.params && this.params.length > 0) {
|
|
33
|
+
Object.assign(body, {
|
|
34
|
+
params: this.params.map((x) => {
|
|
35
|
+
if (x.default) {
|
|
36
|
+
return { [x.name]: x.default };
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
return x.name;
|
|
40
|
+
}
|
|
41
|
+
}),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
Object.assign(body, {
|
|
45
|
+
steps: this.steps.map(({ name, step }) => {
|
|
46
|
+
return { [name]: renderStep(step) };
|
|
47
|
+
}),
|
|
48
|
+
});
|
|
49
|
+
return body;
|
|
50
|
+
}
|
|
51
|
+
*iterateStepsDepthFirst() {
|
|
52
|
+
const visited = new Set();
|
|
53
|
+
function* visitPreOrder(step) {
|
|
54
|
+
if (!visited.has(step)) {
|
|
55
|
+
visited.add(step);
|
|
56
|
+
yield step;
|
|
57
|
+
for (const x of nestedSteps(step.step).flat()) {
|
|
58
|
+
yield* visitPreOrder(x);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
for (const step of this.steps) {
|
|
63
|
+
yield* visitPreOrder(step);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Print the workflow as a YAML string.
|
|
69
|
+
*/
|
|
70
|
+
export function toYAMLString(workflow) {
|
|
71
|
+
return YAML.stringify(workflow.render(), {
|
|
72
|
+
lineWidth: 100,
|
|
73
|
+
});
|
|
74
|
+
}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import { program } from 'commander';
|
|
4
|
+
import { transpile } from './transpiler/index.js';
|
|
5
|
+
import { WorkflowSyntaxError } from './errors.js';
|
|
6
|
+
import { TSError } from '@typescript-eslint/typescript-estree';
|
|
7
|
+
function parseArgs() {
|
|
8
|
+
program
|
|
9
|
+
.name('ts2workflow')
|
|
10
|
+
.version('0.1.0')
|
|
11
|
+
.description('Transpile a Typescript program into GCP Workflows YAML syntax.')
|
|
12
|
+
.argument('[FILES]', 'Path to source file(s) to compile. If not given, reads from stdin.');
|
|
13
|
+
program.parse();
|
|
14
|
+
return {
|
|
15
|
+
sourceFiles: program.args,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function cliMain() {
|
|
19
|
+
const args = parseArgs();
|
|
20
|
+
let files = [];
|
|
21
|
+
if (args.sourceFiles.length === 0) {
|
|
22
|
+
files = ['-'];
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
files = args.sourceFiles;
|
|
26
|
+
}
|
|
27
|
+
files.forEach((inputFile) => {
|
|
28
|
+
const inp = inputFile === '-' ? process.stdin.fd : inputFile;
|
|
29
|
+
let sourceCode = '';
|
|
30
|
+
try {
|
|
31
|
+
sourceCode = fs.readFileSync(inp, 'utf8');
|
|
32
|
+
console.log(transpile(sourceCode));
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
if (isIoError(err, 'ENOENT')) {
|
|
36
|
+
console.error(`Error: "${inp}" not found`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
else if (isIoError(err, 'EISDIR')) {
|
|
40
|
+
console.error(`Error: "${inp}" is a directory`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
else if (isIoError(err, 'EAGAIN') && inp === process.stdin.fd) {
|
|
44
|
+
// Reading from stdin if there's no input causes error. This is a bug in node
|
|
45
|
+
console.error('Error: Failed to read from stdin');
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
else if (err instanceof WorkflowSyntaxError) {
|
|
49
|
+
prettyPrintSyntaxError(err, inputFile, sourceCode);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
else if (err instanceof TSError) {
|
|
53
|
+
prettyPrintSyntaxError(err, inputFile, sourceCode);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
throw err;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function isIoError(err, errorCode) {
|
|
63
|
+
return err instanceof Error && 'code' in err && err.code == errorCode;
|
|
64
|
+
}
|
|
65
|
+
function prettyPrintSyntaxError(exception, inputFile, sourceCode) {
|
|
66
|
+
console.error(errorDisplay(inputFile, sourceCode, exception.location));
|
|
67
|
+
console.error(`${exception.message}`);
|
|
68
|
+
}
|
|
69
|
+
function errorDisplay(filename, sourceCode, location) {
|
|
70
|
+
const lines = [];
|
|
71
|
+
const prettyFilename = filename === '-' ? '<stdin>' : filename;
|
|
72
|
+
if (typeof location?.start === 'undefined' ||
|
|
73
|
+
typeof location?.end === 'undefined' ||
|
|
74
|
+
isNaN(location?.start.line) ||
|
|
75
|
+
isNaN(location?.end.line)) {
|
|
76
|
+
lines.push(`File ${prettyFilename}:`);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
lines.push(`File ${prettyFilename}, line ${location.start.line}, column ${location.start.column + 1}:`);
|
|
80
|
+
}
|
|
81
|
+
const highlightedLine = highlightedSourceCodeLine(sourceCode, location?.start?.line, location?.start?.column, location?.start?.line === location?.end?.line
|
|
82
|
+
? location?.end?.column
|
|
83
|
+
: undefined);
|
|
84
|
+
if (highlightedLine.length > 0) {
|
|
85
|
+
lines.push(highlightedLine);
|
|
86
|
+
lines.push('');
|
|
87
|
+
}
|
|
88
|
+
return lines.join('\n');
|
|
89
|
+
}
|
|
90
|
+
function highlightedSourceCodeLine(sourceCode, lineNumber, start, end) {
|
|
91
|
+
if (typeof lineNumber === 'undefined' ||
|
|
92
|
+
typeof start === 'undefined' ||
|
|
93
|
+
isNaN(start)) {
|
|
94
|
+
return '';
|
|
95
|
+
}
|
|
96
|
+
const lines = sourceCode.split('\n');
|
|
97
|
+
const sourceLine = lines[lineNumber - 1];
|
|
98
|
+
if (typeof sourceLine === 'undefined') {
|
|
99
|
+
return '';
|
|
100
|
+
}
|
|
101
|
+
let markerLength;
|
|
102
|
+
if (typeof end === 'undefined') {
|
|
103
|
+
markerLength = sourceLine.length - start;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
markerLength = Math.min(end - start + 1, sourceLine.length - start);
|
|
107
|
+
}
|
|
108
|
+
const markerLine = `${' '.repeat(start)}${'^'.repeat(markerLength)}`;
|
|
109
|
+
return `${sourceLine}\n${markerLine}`;
|
|
110
|
+
}
|
|
111
|
+
if (import.meta.url.endsWith(process.argv[1]) ||
|
|
112
|
+
process.argv[1].endsWith('/ts2workflows')) {
|
|
113
|
+
cliMain();
|
|
114
|
+
}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface SourceCodeLocation {
|
|
2
|
+
start: {
|
|
3
|
+
line: number;
|
|
4
|
+
column: number;
|
|
5
|
+
};
|
|
6
|
+
end: {
|
|
7
|
+
line: number;
|
|
8
|
+
column: number;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export declare class WorkflowSyntaxError extends Error {
|
|
12
|
+
location: SourceCodeLocation;
|
|
13
|
+
constructor(message: string, location: SourceCodeLocation);
|
|
14
|
+
}
|
|
15
|
+
export declare class InternalTranspilingError extends Error {
|
|
16
|
+
constructor(message: string);
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IACvC,GAAG,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CACtC;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,EAAE,kBAAkB,CAAA;gBAEhB,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,kBAAkB;CAI1D;AAED,qBAAa,wBAAyB,SAAQ,KAAK;gBACrC,OAAO,EAAE,MAAM;CAG5B"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export class WorkflowSyntaxError extends Error {
|
|
2
|
+
location;
|
|
3
|
+
constructor(message, location) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.location = location;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export class InternalTranspilingError extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(`Internal error: ${message}`);
|
|
11
|
+
}
|
|
12
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACjD,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,aAAa,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asserts.d.ts","sourceRoot":"","sources":["../../src/transpiler/asserts.ts"],"names":[],"mappings":"AAEA,wBAAgB,UAAU,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAM7E;AAED,wBAAgB,oBAAoB,CAClC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EACtB,qBAAqB,EAAE,MAAM,EAAE,GAC9B,IAAI,CAMN"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { InternalTranspilingError } from '../errors.js';
|
|
2
|
+
export function assertType(node, expectedType) {
|
|
3
|
+
if (node?.type !== expectedType) {
|
|
4
|
+
throw new InternalTranspilingError(`Expected ${expectedType}, got ${node?.type}`);
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export function assertOneOfManyTypes(node, expectAnyOfTheseTypes) {
|
|
8
|
+
if (!expectAnyOfTheseTypes.includes(node?.type)) {
|
|
9
|
+
throw new InternalTranspilingError(`Expected ${expectAnyOfTheseTypes.join(' or ')}, got ${node?.type}`);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Expression, Primitive } from '../ast/expressions.js';
|
|
2
|
+
export declare function convertExpression(instance: any): Expression;
|
|
3
|
+
export declare function convertObjectExpression(node: any): Record<string, Primitive | Expression>;
|
|
4
|
+
export declare function convertObjectAsExpressionValues(node: any): Record<string, Expression>;
|
|
5
|
+
export declare function convertMemberExpression(node: any): Expression;
|
|
6
|
+
//# sourceMappingURL=expressions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expressions.d.ts","sourceRoot":"","sources":["../../src/transpiler/expressions.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,UAAU,EAGV,SAAS,EAMV,MAAM,uBAAuB,CAAA;AAiB9B,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,GAAG,GAAG,UAAU,CAO3D;AAED,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,GAAG,GACR,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,UAAU,CAAC,CAgCxC;AAED,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,GAAG,GACR,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAU5B;AAwKD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,GAAG,GAAG,UAAU,CAS7D"}
|