harmonyc 0.6.0-8 → 0.7.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/js_api/js_api.js DELETED
@@ -1,83 +0,0 @@
1
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
- };
6
- var _FeatureContext_actions, _FeatureContext_responses, _FeatureContext_parameterTypeRegistry;
7
- import { CucumberExpression, ParameterTypeRegistry, } from '@cucumber/cucumber-expressions';
8
- class Definition {
9
- constructor(expr, fn) {
10
- this.expr = expr;
11
- this.fn = fn;
12
- }
13
- }
14
- const featureDefs = new Map();
15
- class FeatureContext {
16
- constructor() {
17
- _FeatureContext_actions.set(this, []);
18
- _FeatureContext_responses.set(this, []);
19
- _FeatureContext_parameterTypeRegistry.set(this, new ParameterTypeRegistry());
20
- this.Action = ((s, fn) => {
21
- if (typeof fn === 'function') {
22
- const expr = new CucumberExpression(s, __classPrivateFieldGet(this, _FeatureContext_parameterTypeRegistry, "f"));
23
- const def = new Definition(expr, fn);
24
- __classPrivateFieldGet(this, _FeatureContext_actions, "f").push(def);
25
- return;
26
- }
27
- // call the action
28
- const matches = __classPrivateFieldGet(this, _FeatureContext_actions, "f").map((def) => def.expr.match(s));
29
- const matching = [...matches.keys()].filter((i) => matches[i]);
30
- if (matching.length === 0) {
31
- throw new Error(`Action not defined: ${s}`);
32
- }
33
- if (matching.length > 1) {
34
- throw new Error(`Ambiguous: ${s}\n${matching
35
- .map((i) => __classPrivateFieldGet(this, _FeatureContext_actions, "f")[i].expr.source)
36
- .join('\n')}`);
37
- }
38
- const match = matches[matching[0]];
39
- const def = __classPrivateFieldGet(this, _FeatureContext_actions, "f")[matching[0]];
40
- const docstring = fn;
41
- return Promise.resolve(def.fn(...match.map((m) => m.getValue(undefined)), docstring));
42
- });
43
- this.Response = ((s, fn) => {
44
- if (typeof fn === 'function') {
45
- const expr = new CucumberExpression(s, __classPrivateFieldGet(this, _FeatureContext_parameterTypeRegistry, "f"));
46
- const def = new Definition(expr, fn);
47
- __classPrivateFieldGet(this, _FeatureContext_responses, "f").push(def);
48
- return;
49
- }
50
- // call the action
51
- const matches = __classPrivateFieldGet(this, _FeatureContext_responses, "f").map((def) => def.expr.match(s));
52
- const matching = [...matches.keys()].filter((i) => matches[i]);
53
- if (matching.length === 0) {
54
- throw new Error(`Response not defined: ${s}`);
55
- }
56
- if (matching.length > 1) {
57
- throw new Error(`Ambiguous: ${s}\n${matching
58
- .map((i) => __classPrivateFieldGet(this, _FeatureContext_responses, "f")[i].expr.source)
59
- .join('\n')}`);
60
- }
61
- const match = matches[matching[0]];
62
- const def = __classPrivateFieldGet(this, _FeatureContext_responses, "f")[matching[0]];
63
- const docstring = fn;
64
- return Promise.resolve(def.fn(...match.map((m) => m.getValue(undefined)), docstring));
65
- });
66
- }
67
- }
68
- _FeatureContext_actions = new WeakMap(), _FeatureContext_responses = new WeakMap(), _FeatureContext_parameterTypeRegistry = new WeakMap();
69
- export function Feature(s, fn) {
70
- if (!fn) {
71
- // instantiate the feature
72
- const fn = featureDefs.get(s);
73
- if (!fn)
74
- throw new Error(`Feature not found: ${s}`);
75
- const ctx = new FeatureContext();
76
- fn(ctx);
77
- return ctx;
78
- }
79
- else {
80
- // (re)define the feature
81
- featureDefs.set(s, fn);
82
- }
83
- }
@@ -1,33 +0,0 @@
1
- export class Gherkin {
2
- constructor(outFile) {
3
- this.outFile = outFile;
4
- }
5
- feature(feature) {
6
- this.outFile.print(`Feature: ${feature.name}`);
7
- this.outFile.print('');
8
- this.outFile.indent(() => {
9
- for (const test of feature.tests) {
10
- test.toCode(this);
11
- }
12
- });
13
- }
14
- test(t) {
15
- this.outFile.print(`Scenario: ${t.name}`);
16
- this.outFile.indent(() => {
17
- for (const step of t.steps) {
18
- step.toCode(this);
19
- }
20
- });
21
- this.outFile.print('');
22
- }
23
- phrase(p) {
24
- this.outFile.print(`${p.keyword} ${p.text} || ${p.feature}`);
25
- if (p.docstring !== undefined) {
26
- this.outFile.indent(() => {
27
- this.outFile.print(`"""`);
28
- this.outFile.print(...p.docstring.split('\n'));
29
- this.outFile.print(`"""`);
30
- });
31
- }
32
- }
33
- }
@@ -1,87 +0,0 @@
1
- import { basename } from 'path';
2
- export class NodeTest {
3
- constructor(tf, sf) {
4
- this.tf = tf;
5
- this.sf = sf;
6
- this.framework = 'vitest';
7
- this.phrases = [];
8
- }
9
- feature(feature) {
10
- const stepsModule = './' + basename(this.sf.name.replace(/.(js|ts)$/, ''));
11
- this.phrases = [];
12
- if (this.framework === 'vitest') {
13
- this.tf.print(`import { test, expect } from 'vitest';`);
14
- this.tf.print(`import { Feature } from 'harmonyc/test';`);
15
- this.tf.print(`import ${str(stepsModule)};`);
16
- }
17
- for (const test of feature.tests) {
18
- test.toCode(this);
19
- }
20
- this.sf.print(`import { Feature } from 'harmonyc/test';`);
21
- this.sf.print('');
22
- this.sf.print(`Feature(${str(feature.name)}, ({ Action, Response }) => {`);
23
- this.sf.indent(() => {
24
- for (const phrase of this.phrases) {
25
- this.sf.print(`${capitalize(phrase.kind)}(${str(phrase.text)}, () => {`);
26
- this.sf.indent(() => {
27
- this.sf.print(`throw new Error(${str(`Pending: ${phrase.text}`)})`);
28
- });
29
- this.sf.print('})');
30
- }
31
- });
32
- this.sf.print('})');
33
- }
34
- test(t) {
35
- this.featureVars = new Map();
36
- this.tf.print(`test('${t.name}', async (context) => {`);
37
- this.tf.indent(() => {
38
- for (const step of t.steps) {
39
- step.toCode(this);
40
- }
41
- });
42
- this.tf.print('})');
43
- this.tf.print('');
44
- }
45
- phrase(p) {
46
- if (!this.phrases.some((x) => x.text === p.text))
47
- this.phrases.push(p);
48
- const feature = p.feature.name;
49
- let f = this.featureVars.get(feature);
50
- if (!f) {
51
- f = toId(feature, this.featureVars);
52
- this.tf.print(`const ${f} = Feature(${str(feature)})`);
53
- }
54
- const docstring = p.docstring ? ', \n' + templateStr(p.docstring) : '';
55
- this.tf.print(`await ${f}.${capitalize(p.kind)}(${str(p.text)}${docstring})`);
56
- }
57
- }
58
- function str(s) {
59
- let r = JSON.stringify(s);
60
- return r;
61
- }
62
- function templateStr(s) {
63
- return '`' + s.replace(/([`$])/g, '\\$1') + '`';
64
- }
65
- function capitalize(s) {
66
- return s.charAt(0).toUpperCase() + s.slice(1);
67
- }
68
- function toId(s, previous) {
69
- if (previous.has(s))
70
- return previous.get(s);
71
- let base = pascalCase(s);
72
- let id = base;
73
- if ([...previous.values()].includes(id)) {
74
- let i = 1;
75
- while ([...previous.values()].includes(id + i))
76
- i++;
77
- id = base + i;
78
- }
79
- previous.set(s, id);
80
- return id;
81
- }
82
- function pascalCase(s) {
83
- return s
84
- .split(/[^a-zA-Z0-9]/)
85
- .map(capitalize)
86
- .join('');
87
- }
package/model.js DELETED
@@ -1,216 +0,0 @@
1
- import { Routers } from './Router.js';
2
- export class Feature {
3
- constructor(name) {
4
- this.name = name;
5
- this.root = new Section();
6
- this.definitions = new Map();
7
- this.prelude = '';
8
- }
9
- get tests() {
10
- return makeTests(this.root);
11
- }
12
- toCode(cg) {
13
- cg.feature(this);
14
- }
15
- }
16
- export class Branch {
17
- constructor(children = []) {
18
- this.isFork = false;
19
- this.isEnd = false;
20
- this.children = children;
21
- children.forEach((child) => (child.parent = this));
22
- }
23
- setFork(isFork) {
24
- this.isFork = isFork;
25
- return this;
26
- }
27
- setFeature(feature) {
28
- for (const child of this.children)
29
- child.setFeature(feature);
30
- return this;
31
- }
32
- addChild(child, index = this.children.length) {
33
- this.children.splice(index, 0, child);
34
- child.parent = this;
35
- return child;
36
- }
37
- get isLeaf() {
38
- return this.children.length === 0;
39
- }
40
- get successors() {
41
- if (!this.isLeaf)
42
- return this.children.filter((c, i) => i === 0 || c.isFork);
43
- else {
44
- if (this.isEnd)
45
- return [];
46
- const next = this.nextNonForkAncestorSibling;
47
- if (next)
48
- return [next];
49
- return [];
50
- }
51
- }
52
- get nextNonForkAncestorSibling() {
53
- if (!this.parent)
54
- return undefined;
55
- const { nextSibling } = this;
56
- if (nextSibling && !nextSibling.isFork)
57
- return nextSibling;
58
- return this.parent.nextNonForkAncestorSibling;
59
- }
60
- get nextSibling() {
61
- if (!this.parent)
62
- return undefined;
63
- return this.parent.children[this.siblingIndex + 1];
64
- }
65
- get siblingIndex() {
66
- var _a, _b;
67
- return (_b = (_a = this.parent) === null || _a === void 0 ? void 0 : _a.children.indexOf(this)) !== null && _b !== void 0 ? _b : -1;
68
- }
69
- }
70
- export class Step extends Branch {
71
- constructor(action = '', responses = [], children, isFork = false) {
72
- super(children);
73
- this.action = new Action(action);
74
- this.responses = responses.map((response) => new Response(response));
75
- this.isFork = isFork;
76
- }
77
- get phrases() {
78
- return [this.action, ...this.responses];
79
- }
80
- toCode(cg) {
81
- for (const phrase of this.phrases) {
82
- phrase.toCode(cg);
83
- }
84
- }
85
- setFeature(feature) {
86
- this.action.setFeature(feature);
87
- for (const response of this.responses)
88
- response.setFeature(feature);
89
- return super.setFeature(feature);
90
- }
91
- }
92
- export class State {
93
- constructor(text = '') {
94
- this.text = text;
95
- }
96
- }
97
- export class Label {
98
- constructor(text = '') {
99
- this.text = text;
100
- }
101
- }
102
- export class Section extends Branch {
103
- constructor(label = '', children, isFork = false) {
104
- super(children);
105
- this.label = new Label(label);
106
- this.isFork = isFork;
107
- }
108
- }
109
- export class Phrase {
110
- constructor(text = '') {
111
- this.text = text;
112
- }
113
- setFeature(feature) {
114
- this.feature = feature;
115
- }
116
- get keyword() {
117
- return this.kind === 'action' ? 'When' : 'Then';
118
- }
119
- toCode(cg) {
120
- return cg.phrase(this);
121
- }
122
- definition() {
123
- const key = this.kind === 'action' ? this.text : `=> ${this.text}`;
124
- let args;
125
- let code;
126
- for (const [ce, c] of this.feature.definitions.entries()) {
127
- const m = ce.match(key);
128
- if (!m)
129
- continue;
130
- if (args !== undefined)
131
- throw new Error(`Ambiguous definition: ${key}`);
132
- args = m;
133
- code = c;
134
- }
135
- if (args === undefined)
136
- return undefined;
137
- return code.replace(/\$([$_1-9])/g, (s, varName) => {
138
- var _a;
139
- if (varName.match(/[1-9]/))
140
- return JSON.stringify(args[parseInt(varName) - 1].getValue(undefined));
141
- else if (varName === '_')
142
- return JSON.stringify((_a = this.docstring) !== null && _a !== void 0 ? _a : '');
143
- else if (varName === '$')
144
- return '$';
145
- else
146
- return s;
147
- });
148
- }
149
- }
150
- export class Action extends Phrase {
151
- constructor() {
152
- super(...arguments);
153
- this.kind = 'action';
154
- }
155
- }
156
- export class Response extends Phrase {
157
- constructor() {
158
- super(...arguments);
159
- this.kind = 'response';
160
- }
161
- }
162
- export class Precondition extends Branch {
163
- constructor(state = '') {
164
- super();
165
- this.state = new State();
166
- this.state.text = state;
167
- }
168
- }
169
- export function makeTests(root) {
170
- const routers = new Routers(root);
171
- const tests = [];
172
- let ic = routers.getIncompleteCount();
173
- let newIc;
174
- do {
175
- const newTest = new Test(root, routers.nextWalk());
176
- newIc = routers.getIncompleteCount();
177
- if (newIc < ic)
178
- tests.push(newTest);
179
- ic = newIc;
180
- } while (ic > 0);
181
- // sort by order of appearance of the last branch
182
- const branchIndex = new Map();
183
- let i = 0;
184
- function walk(branch) {
185
- branchIndex.set(branch, i++);
186
- for (const child of branch.children)
187
- walk(child);
188
- }
189
- walk(root);
190
- tests.sort((a, b) => branchIndex.get(a.last) - branchIndex.get(b.last));
191
- tests.forEach((test, i) => (test.testNumber = `T${i + 1}`));
192
- return tests;
193
- }
194
- export class Test {
195
- constructor(root, branches) {
196
- this.root = root;
197
- this.branches = branches;
198
- }
199
- get steps() {
200
- return this.branches.filter((b) => b instanceof Step);
201
- }
202
- get last() {
203
- return this.steps[this.steps.length - 1];
204
- }
205
- get labels() {
206
- return this.branches
207
- .filter((b) => b instanceof Section)
208
- .map((s) => s.label.text);
209
- }
210
- get name() {
211
- return `${this.testNumber}${this.labels.length > 0 ? ' - ' : ''}${this.labels.join(' - ')}`;
212
- }
213
- toCode(cg) {
214
- cg.test(this);
215
- }
216
- }
package/syntax.js DELETED
@@ -1,102 +0,0 @@
1
- import remarkParse from 'remark-parse';
2
- import { unified } from 'unified';
3
- import { Feature, Section, Step } from './model.js';
4
- export function parse({ fileName, src, }) {
5
- const tree = unified().use(remarkParse).parse(src);
6
- const rootNodes = tree.children;
7
- const feature = new Feature('');
8
- const headings = [feature.root];
9
- let name;
10
- for (let i = 0; i < rootNodes.length; i++) {
11
- const node = rootNodes[i];
12
- if (node.type === 'heading') {
13
- if (node.depth === 1 && name === undefined) {
14
- name = textContent(node);
15
- }
16
- const level = node.depth;
17
- const section = new Section(textContent(node), [], true);
18
- setLocation(section, node, fileName);
19
- const last = headings.slice(0, level).reverse().find(Boolean);
20
- last.addChild(section);
21
- headings[level] = section;
22
- headings.length = level + 1;
23
- }
24
- else {
25
- const last = headings[headings.length - 1];
26
- for (const branch of topLevel(node))
27
- last.addChild(branch);
28
- }
29
- }
30
- feature.name = name !== null && name !== void 0 ? name : fileName;
31
- feature.root.setFeature(feature);
32
- return feature;
33
- function topLevel(node) {
34
- if (node.type === 'paragraph')
35
- return [];
36
- if (node.type == 'list')
37
- return list(node);
38
- return [];
39
- }
40
- function list(node) {
41
- const isFork = node.ordered === false;
42
- return node.children.map((item) => listItem(item, isFork));
43
- }
44
- function listItem(node, isFork) {
45
- const first = node.children[0];
46
- const text = textContent(first);
47
- let branch;
48
- if (first.type === 'heading') {
49
- branch = new Section(text, [], isFork);
50
- }
51
- else {
52
- const [action, ...responses] = text.split(/(?:^| )=>(?: |$)/);
53
- const step = (branch = new Step(action, responses.filter(Boolean), [], isFork));
54
- setLocation(step.action, first, fileName);
55
- for (const response of step.responses) {
56
- // todo separate locations along =>'s
57
- setLocation(response, first, fileName);
58
- }
59
- let i = 0;
60
- for (const child of node.children.slice(1)) {
61
- if (child.type === 'list')
62
- break;
63
- if (child.type === 'code') {
64
- if (step.phrases[i])
65
- step.phrases[i].docstring = child.value;
66
- else
67
- break;
68
- ++i;
69
- }
70
- }
71
- }
72
- for (const child of node.children) {
73
- if (child.type === 'list') {
74
- for (const b of list(child))
75
- branch.addChild(b);
76
- }
77
- }
78
- setLocation(branch, node, fileName);
79
- return branch;
80
- }
81
- }
82
- function textContent(node) {
83
- if (!node)
84
- return '';
85
- if (node.type === 'text') {
86
- return node.value.split(/\s+/).filter(Boolean).join(' ');
87
- }
88
- if (node.type === 'list')
89
- return '';
90
- if (!('children' in node))
91
- return '';
92
- return node.children.map(textContent).filter(Boolean).join(' ');
93
- }
94
- function setLocation(model, node, fileName) {
95
- if (!node.position)
96
- return;
97
- model.location = {
98
- line: node.position.start.line,
99
- column: node.position.start.column,
100
- fileName,
101
- };
102
- }
package/watch.js DELETED
@@ -1,11 +0,0 @@
1
- import { watch } from 'node:fs';
2
- import { compileFile, compileFiles } from './compiler.js';
3
- export async function watchFiles(patterns) {
4
- const { fns, outFns } = await compileFiles(patterns);
5
- for (const file of fns) {
6
- watch(file, () => {
7
- compileFile(file);
8
- });
9
- }
10
- return outFns;
11
- }
File without changes
File without changes