bio-dts 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/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # bio-dts
2
+
3
+ [![CI](https://github.com/nikku/bio-dts/actions/workflows/CI.yml/badge.svg)](https://github.com/nikku/bio-dts/actions/workflows/CI.yml)
4
+
5
+ Utilities to generate sane and clean type definitions from JavaScript files.
6
+
7
+ ## About
8
+
9
+ This module provides `pre` and `post` processing helpers to a type definition pipeline, as well as a simple [generator cli](#usage).
10
+
11
+
12
+ ## Usage
13
+
14
+ ```sh
15
+ npx bio-dts --outDir dist/types -r lib
16
+ ```
17
+
18
+ ## API
19
+
20
+ ```javascript
21
+ import {
22
+ preTransform,
23
+ postTransform,
24
+ generateTypes
25
+ } from 'bio-dts';
26
+
27
+ // transform JS so it keeps the shape,
28
+ // but is properly digestable by the typescript
29
+ // compiler
30
+ const transformedCode = preTransform(jsCode);
31
+
32
+ // post process typescript compiler type code
33
+ // removing internals, and fixing up the definitions
34
+ const transformedCode = postTransform(tsCode);
35
+
36
+ // execute the full pipeline, including invoking the
37
+ // typescript compiler
38
+ generateTypes(files, {
39
+ outDir: 'dist'
40
+ });
41
+ ```
package/bin/bio-dts.js ADDED
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env node
2
+
3
+ import ts from 'typescript';
4
+
5
+ import glob from 'tiny-glob';
6
+
7
+ import path from 'node:path';
8
+ import fs from 'node:fs';
9
+
10
+ import generateTypes from '../lib/generate-types.js';
11
+
12
+ async function run() {
13
+ const args = process.argv.slice(2);
14
+
15
+ const {
16
+ options,
17
+ fileNames
18
+ } = ts.parseCommandLine(args);
19
+
20
+ const help = args.includes('--help') || args.includes('-h');
21
+ const verbose = args.includes('--verbose');
22
+ const recursive = args.includes('--recursive') || args.includes('-r');
23
+
24
+ if (help) {
25
+ console.log(`Usage: bio-dts [options] [...filesOrGlobs]
26
+
27
+ Options:
28
+ --recursive, -r recurse into directories
29
+ --verbose enable verbose logging
30
+
31
+ Additional options will be passed to the typescript generator.
32
+
33
+ Examples:
34
+ $ bio-dts --outDir dist -r lib
35
+ $ bio-dts 'lib/**/*.js'
36
+ `);
37
+
38
+ return;
39
+ }
40
+
41
+ const globPattern = recursive ? '**/*.js' : '*.js';
42
+
43
+ const files = [];
44
+
45
+ for (const fileName of fileNames) {
46
+
47
+ try {
48
+ if (fs.statSync(fileName).isDirectory) {
49
+
50
+ const globbedFiles = await glob(globPattern, { cwd: fileName, filesOnly: true });
51
+
52
+ files.push(...globbedFiles.map(f => path.join(fileName, f)));
53
+ } else {
54
+ files.push(fileName);
55
+ }
56
+ } catch (err) {
57
+
58
+ // ignore
59
+ }
60
+ }
61
+
62
+ console.log(`Generating types for ${files.length} files...`);
63
+
64
+ generateTypes(files, {
65
+ allowJs: true,
66
+ declaration: true,
67
+ emitDeclarationOnly: true,
68
+ ...options,
69
+ verbose
70
+ });
71
+
72
+ console.log('Done.');
73
+ }
74
+
75
+ run().catch(err => {
76
+ console.error(err);
77
+
78
+ process.exit(1);
79
+ });
package/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { default as preTransform } from './lib/pre-transform.js';
2
+
3
+ export { default as postTransform } from './lib/post-transform.js';
4
+
5
+ export { default as generateTypes } from './lib/generate-types.js';
@@ -0,0 +1,62 @@
1
+ import path from 'node:path';
2
+
3
+ import ts from 'typescript';
4
+
5
+ import preTransform from './pre-transform.js';
6
+
7
+ import postTransform from './post-transform.js';
8
+
9
+
10
+ /**
11
+ * @param {string[]} fileNames
12
+ * @param {ts.CompilerOptions} options
13
+ */
14
+ export default function generateTypes(fileNames, options) {
15
+
16
+ const names = new Set(fileNames.map((p) => path.resolve(p)));
17
+
18
+ const host = ts.createCompilerHost(options);
19
+
20
+ host._readFile = host.readFile;
21
+
22
+ /**
23
+ * @param {string} fileName
24
+ *
25
+ * @return {string}
26
+ */
27
+ host.readFile = (fileName) => {
28
+ options.verbose && console.debug('[generate-types] [pre]', fileName);
29
+
30
+ let src = host._readFile(fileName);
31
+
32
+ if (names.has(path.resolve(fileName))) {
33
+ try {
34
+ src = preTransform(src);
35
+ } catch (err) {
36
+ console.error(`failed to parse ${fileName} [pre] with contents ${src}`, err);
37
+ throw err;
38
+ }
39
+ }
40
+
41
+ return src;
42
+ };
43
+
44
+ host._writeFile = host.writeFile;
45
+
46
+ host.writeFile = (fileName, text, ...args) => {
47
+ options.verbose && console.debug('[generate-types] [post]', fileName, text);
48
+
49
+ try {
50
+ text = postTransform(text);
51
+ } catch (err) {
52
+ console.error(`failed to parse ${fileName} [post] with contents ${text}`, err);
53
+ throw err;
54
+ }
55
+
56
+ host._writeFile(fileName, text, ...args);
57
+ };
58
+
59
+ const program = ts.createProgram(fileNames, options, host);
60
+
61
+ program.emit();
62
+ }
@@ -0,0 +1,15 @@
1
+ import { parser } from 'recast/parsers/babel.js';
2
+ import getBabelOptions from 'recast/parsers/_babel_options.js';
3
+
4
+ /**
5
+ * @param {string} source
6
+ * @param {Overrides} [options]
7
+ *
8
+ * @return {import('@babel/types').File}
9
+ */
10
+ export function parse(source, options) {
11
+
12
+ const babelOptions = getBabelOptions.default(options);
13
+ babelOptions.plugins.push([ 'typescript', { dts: true } ]);
14
+ return parser.parse(source, babelOptions);
15
+ }
@@ -0,0 +1 @@
1
+ export * from 'recast/parsers/typescript.js';
@@ -0,0 +1,64 @@
1
+ import { print } from 'recast';
2
+
3
+ import { isArray, isNode, keys, parseDts, path } from './util.js';
4
+
5
+
6
+ /**
7
+ * @param { { name: string, comments?: any[] } }
8
+ *
9
+ * @return {boolean}
10
+ */
11
+ function isPublic(m) {
12
+
13
+ const name = m?.key?.name;
14
+
15
+ if (!name) {
16
+ return true;
17
+ }
18
+
19
+ const comments = m?.comments || [];
20
+
21
+ return !name.startsWith('_') && !comments.some(c => c.value.includes('@private'));
22
+ }
23
+
24
+ /**
25
+ * @param {string} src
26
+ * @return {string}
27
+ */
28
+ export default function transform(src) {
29
+
30
+ const ast = parseDts(src);
31
+ const body = path(ast).get('program', 'body');
32
+
33
+ traverse(body, function(path) {
34
+
35
+ // filter private members
36
+ if (!isPublic(path.value)) {
37
+ path.replace();
38
+ }
39
+
40
+ // TODO(nikku): transform illegal typescript methods into override calls
41
+ });
42
+
43
+ return print(ast).code;
44
+ }
45
+
46
+ function traverse(path, cb) {
47
+ cb(path);
48
+
49
+ if (path.parentPath) {
50
+ const val = path.value;
51
+
52
+ if (!isNode(val) && !isArray(val)) {
53
+ return;
54
+ }
55
+
56
+ for (const key of keys(val).reverse()) {
57
+ if ([ 'loc', 'start', 'end' ].includes(key)) {
58
+ continue;
59
+ }
60
+
61
+ traverse(path.get(key), cb);
62
+ }
63
+ }
64
+ }
@@ -0,0 +1,341 @@
1
+ import { print } from 'recast';
2
+
3
+ import { matcher, parse, path } from './util.js';
4
+
5
+ import {
6
+ builders as b
7
+ } from 'ast-types';
8
+
9
+ function superCall() {
10
+ return b.expressionStatement.from({
11
+ 'type': 'ExpressionStatement',
12
+ 'expression': {
13
+ 'type': 'CallExpression',
14
+ 'callee': {
15
+ 'type': 'Super'
16
+ },
17
+ 'arguments': []
18
+ }
19
+ });
20
+ }
21
+
22
+ function clazzConstructor(cls, comments) {
23
+ const constructor = cls.ctor.value;
24
+
25
+ return b.classMethod.from({
26
+ 'type': 'ClassMethod',
27
+ 'static': false,
28
+ 'key': {
29
+ 'type': 'Identifier',
30
+ 'name': 'constructor'
31
+ },
32
+ 'kind': 'constructor',
33
+ 'params': constructor.params,
34
+ 'body': constructor.body,
35
+ 'comments': comments
36
+ });
37
+ }
38
+
39
+ function clazzDefinition(cls, ctor) {
40
+
41
+ const members = [
42
+ ctor,
43
+ ...cls.members.filter(
44
+ m => m.member.type === 'FunctionExpression'
45
+ ).map(m => {
46
+ return {
47
+ 'type': 'ClassMethod',
48
+ 'static': false,
49
+ 'key': {
50
+ 'type': 'Identifier',
51
+ 'name': m.name
52
+ },
53
+ 'kind': 'method',
54
+ 'params': m.member.params,
55
+ 'body': m.member.body,
56
+ 'comments': m.comments
57
+ };
58
+ })
59
+ ];
60
+
61
+ const staticMembers = cls.staticMembers.map(m => {
62
+
63
+ if (m.member.type === 'FunctionExpression') {
64
+ return {
65
+ 'type': 'ClassMethod',
66
+ 'static': true,
67
+ 'key': {
68
+ 'type': 'Identifier',
69
+ 'name': m.name
70
+ },
71
+ 'kind': 'method',
72
+ 'params': m.member.params,
73
+ 'body': m.member.body,
74
+ 'comments': m.comments
75
+ };
76
+ }
77
+
78
+ return {
79
+ 'type': 'ClassProperty',
80
+ 'static': true,
81
+ 'key': {
82
+ 'type': 'Identifier',
83
+ 'name': m.name
84
+ },
85
+ 'value': m.member,
86
+ 'comments': m.comments
87
+ };
88
+ });
89
+
90
+ return b.classDeclaration.from({
91
+ 'type': 'ClassDeclaration',
92
+ 'id': {
93
+ 'type': 'Identifier',
94
+ 'name': cls.name
95
+ },
96
+ 'superClass': cls.superDeclaration ? {
97
+ 'type': 'Identifier',
98
+ 'name': cls.superDeclaration.clazz
99
+ } : null,
100
+ 'body': {
101
+ 'type': 'ClassBody',
102
+ 'body': [
103
+ ...members,
104
+ ...staticMembers
105
+ ]
106
+ }
107
+ });
108
+ }
109
+
110
+ function splitComment(cls) {
111
+
112
+ const node = cls.node.value;
113
+
114
+ const [ comment ] = node.comments?.slice(-1) || [];
115
+
116
+ const additionalComments = node.comments?.slice(0, -1) || [];
117
+
118
+ const [ intro, ...rest ] = comment?.value?.split(/\* @param/) || [];
119
+
120
+ return {
121
+ ctorComments: rest.length ? [ {
122
+ leading: true,
123
+ trailing: false,
124
+ type: 'CommentBlock',
125
+ value: '*\n * @param' + rest.join('* @param'),
126
+ } ] : [],
127
+ additionalComments: [
128
+ ...additionalComments,
129
+ ...(
130
+ intro ? [
131
+ {
132
+ leading: true,
133
+ type: 'CommentBlock',
134
+ value: intro
135
+ }
136
+ ] : []
137
+ )
138
+ ]
139
+ };
140
+ }
141
+
142
+ /**
143
+ * @param {string} src
144
+ * @return {string}
145
+ */
146
+ export default function transform(src) {
147
+
148
+ const ast = parse(src);
149
+ const body = path(ast).get('program', 'body');
150
+
151
+ const classes = findConstructors(body);
152
+
153
+ for (const cls of classes) {
154
+ const members = cls.members = [
155
+ ...findEmbeddedMethods(cls),
156
+ ...findProtoMethods(cls, body)
157
+ ];
158
+
159
+ const staticMembers = cls.staticMembers = [
160
+ ...findStaticMembers(cls, body)
161
+ ];
162
+
163
+ const superDeclaration = cls.superDeclaration = findSuperDeclarations(cls, body);
164
+
165
+ if (superDeclaration) {
166
+ cls.ctor.get('body', 'body').unshift(superCall());
167
+
168
+ superDeclaration.superCalls.forEach(m => m.node.replace());
169
+ }
170
+
171
+ const {
172
+ ctorComments,
173
+ additionalComments
174
+ } = splitComment(cls);
175
+
176
+ const ctor = clazzConstructor(cls, ctorComments);
177
+
178
+ cls.ctor.replace(clazzDefinition(cls, ctor));
179
+ cls.node.get('comments').replace(additionalComments);
180
+
181
+ members.forEach(m => {
182
+ if (m.member.type === 'FunctionExpression') {
183
+ m.node.replace();
184
+ }
185
+ });
186
+
187
+ staticMembers.forEach(m => {
188
+ m.node.replace();
189
+ });
190
+
191
+ if (superDeclaration) {
192
+ superDeclaration.importDeclarations.forEach(m => m.node.replace());
193
+ superDeclaration.inheritsExpression.node.replace();
194
+ }
195
+ }
196
+
197
+ return print(ast).code;
198
+ }
199
+
200
+ function findSuperDeclarations(cls, nodes) {
201
+
202
+ const importsMatches = matcher`
203
+ import $1 from 'inherits-browser';
204
+ `;
205
+
206
+ const importDeclarations = importsMatches(nodes);
207
+
208
+ for (const [ _, identifier ] of importDeclarations) {
209
+
210
+ const inheritsMatches = matcher`
211
+ ${identifier.value.name}(${cls.name}, $1);
212
+ `;
213
+
214
+ const inheritsExpressions = inheritsMatches(nodes);
215
+
216
+ if (inheritsExpressions.length > 1) {
217
+ throw new Error('more than one <inherits> declaration');
218
+ }
219
+
220
+ if (inheritsExpressions.length === 1) {
221
+
222
+ const clazz = inheritsExpressions[0][1].value.name;
223
+
224
+ const inheritsExpression = {
225
+ node: inheritsExpressions[0][0]
226
+ };
227
+
228
+ const superCallMatches = matcher`
229
+ ${clazz}.call($$$);
230
+ `;
231
+
232
+ const superCalls = superCallMatches(cls.ctor.get('body', 'body'));
233
+
234
+ return {
235
+ importDeclarations: importDeclarations.map(m => ({ node: m[0] })),
236
+ superCalls: superCalls.map(m => ({ node: m[0] })),
237
+ clazz,
238
+ inheritsExpression
239
+ };
240
+ }
241
+ }
242
+
243
+ }
244
+
245
+ function findConstructors(nodes) {
246
+
247
+ const fns = matcher`
248
+ function $1($$$) {
249
+ $$$
250
+ }
251
+ `;
252
+
253
+ const namedExportFns = matcher`
254
+ export function $1($$$) {
255
+ $$$
256
+ }
257
+ `;
258
+
259
+ const defaultExportFns = matcher`
260
+ export default function $1($$$) {
261
+ $$$
262
+ }
263
+ `;
264
+
265
+ return [
266
+ ...fns(nodes),
267
+ ...namedExportFns(nodes).map(
268
+ m => [ m[0].get('declaration'), m[1], m[0] ]
269
+ ),
270
+ ...defaultExportFns(nodes).map(
271
+ m => [ m[0].get('declaration'), m[1], m[0] ]
272
+ )
273
+ ].map(match => {
274
+
275
+ const [ ctor, identifier, node = ctor ] = match;
276
+
277
+ const name = identifier.value.name;
278
+
279
+ if (/^[A-Z]/.test(name)) {
280
+ return {
281
+ name,
282
+ ctor,
283
+ node
284
+ };
285
+ }
286
+ }).filter(n => n);
287
+ }
288
+
289
+ function findStaticMembers(cls, nodes) {
290
+
291
+ const names = matcher`
292
+ ${cls.name}.$1 = $2;
293
+ `;
294
+
295
+ return names(nodes).map(match => {
296
+ const [ node, identifier, member ] = match;
297
+
298
+ return {
299
+ name: identifier.value.name,
300
+ member: member.value,
301
+ comments: node.get('comments').value,
302
+ node
303
+ };
304
+ });
305
+ }
306
+
307
+ function findProtoMethods(cls, nodes) {
308
+
309
+ const names = matcher`
310
+ ${cls.name}.prototype.$1 = $2;
311
+ `;
312
+
313
+ return names(nodes).map(match => {
314
+ const [ node, identifier, member ] = match;
315
+
316
+ return {
317
+ name: identifier.value.name,
318
+ member: member.value,
319
+ comments: node.get('comments').value,
320
+ node
321
+ };
322
+ });
323
+ }
324
+
325
+ function findEmbeddedMethods(cls) {
326
+
327
+ const memberDeclarations = matcher`
328
+ this.$1 = $2;
329
+ `;
330
+
331
+ return memberDeclarations(cls.ctor.get('body', 'body')).map(match => {
332
+ const [ node, identifier, member ] = match;
333
+
334
+ return {
335
+ name: identifier.value.name,
336
+ member: member.value,
337
+ comments: node.get('comments').value,
338
+ node
339
+ };
340
+ });
341
+ }
package/lib/util.js ADDED
@@ -0,0 +1,262 @@
1
+ import { parse as recastParse } from 'recast';
2
+ import { Path as PathConstructor } from 'ast-types';
3
+
4
+ /**
5
+ * @typedef { {
6
+ * get(...any) => any,
7
+ * value: any
8
+ * } } Path
9
+ */
10
+
11
+ import * as typescriptParser from './parsers/typescript.js';
12
+ import * as typescriptDtsParser from './parsers/typescript-dts.js';
13
+
14
+ const DBG = /match/.test(process?.env?.LOG_DEBUG);
15
+
16
+ /**
17
+ * Return numeric or string keys of the given object.
18
+ *
19
+ * @param {any} obj
20
+ * @return {(string|number)[]}
21
+ */
22
+ export function keys(obj) {
23
+ return Object.keys(obj).map(k => {
24
+ const n = parseInt(k, 10);
25
+
26
+ return isNaN(n) ? k : n;
27
+ });
28
+ }
29
+
30
+ /**
31
+ * @param {any} arr
32
+ *
33
+ * @return {boolean}
34
+ */
35
+ export function isArray(arr) {
36
+ return Array.isArray(arr);
37
+ }
38
+
39
+ export function isNode(obj) {
40
+ return Object.prototype.toString.call(obj) === '[object Object]';
41
+ }
42
+
43
+ export function hasProperty(obj, property) {
44
+ return Object.prototype.hasOwnProperty.call(obj, property);
45
+ }
46
+
47
+ /**
48
+ * @param {string} code
49
+ *
50
+ * @return {any}
51
+ */
52
+ export function parse(code) {
53
+ return recastParse(code, {
54
+ parser: typescriptParser
55
+ });
56
+ }
57
+
58
+ export function parseDts(code) {
59
+ return recastParse(code, {
60
+ parser: typescriptDtsParser
61
+ });
62
+ }
63
+
64
+ /**
65
+ * @param {any} node
66
+ *
67
+ * @return {Path}
68
+ */
69
+ export function path(node) {
70
+ return new PathConstructor(node);
71
+ }
72
+
73
+ /**
74
+ * @param {string[]} strings
75
+ * @param {...any} args
76
+ *
77
+ * @return { (node) => [][] }
78
+ */
79
+ export function matcher(strings, ...args) {
80
+
81
+ const code = strings.reduce((code, str, i) => {
82
+
83
+ code += str;
84
+
85
+ if (args[i]) {
86
+ code += args[i];
87
+ }
88
+
89
+ return code;
90
+ }, '');
91
+
92
+ const ast = parse(code, {
93
+ parser: typescriptParser
94
+ });
95
+
96
+ const body = path(ast).get('program', 'body');
97
+
98
+ if (body.value.length !== 1) {
99
+ throw new Error(`${ code } is more than one statement!`);
100
+ }
101
+
102
+ const expr = body.get(0);
103
+
104
+ /**
105
+ * Match for wildcards:
106
+ *
107
+ * - block statement with a single $$$
108
+ * - expression statement with $$$
109
+ * - identifier $$$
110
+ * - wrapped as list
111
+ *
112
+ * @param {any} node
113
+ *
114
+ * @return {boolean}
115
+ */
116
+ function wildcard(node) {
117
+
118
+ if (isArray(node) && node.length === 1) {
119
+ node = node[0];
120
+ }
121
+
122
+ if (node && node.type === 'ExpressionStatement') {
123
+ node = node.expression;
124
+ }
125
+
126
+ if (node && node.type === 'Identifier' && node.name === '$$$') {
127
+ return true;
128
+ }
129
+
130
+ }
131
+
132
+ /**
133
+ * Matches ${IDENTIFIER} single node.
134
+ *
135
+ * @param {any} node
136
+ *
137
+ * @return {any|null}
138
+ */
139
+ function ident(node) {
140
+ if (node.type === 'Identifier' && /^\$.+/.test(node.name)) {
141
+ return node.name.substring(1);
142
+ }
143
+
144
+ return null;
145
+ }
146
+
147
+ /**
148
+ * Matches $${IDENTIFIER} many nodes:
149
+ *
150
+ * - block statement with a single $${IDENT}
151
+ * - expression statement with $${IDENT}
152
+ * - identifier $${IDENT}
153
+ * - wrapped as list
154
+ *
155
+ * @param {any} node
156
+ *
157
+ * @return {any|null}
158
+ */
159
+ function anyIdent(node) {
160
+
161
+ if (isArray(node) && node.length === 1) {
162
+ node = node[0];
163
+ }
164
+
165
+ if (node && node.type === 'ExpressionStatement') {
166
+ node = node.expression;
167
+ }
168
+
169
+ if (node && node.type === 'Identifier' && /^\$\$.+/.test(node.name)) {
170
+ return node.name.substring(2);
171
+ }
172
+
173
+ return null;
174
+ }
175
+
176
+ /**
177
+ * @param {Path} node
178
+ * @param {Path} expectedNode
179
+ * @param {Path[]} results
180
+ *
181
+ * @return {Path[] || null}
182
+ */
183
+ function matchNode(node, expectedNode, results = []) {
184
+
185
+ if (!node.value) {
186
+ DBG && console.log('BAIL no value');
187
+ return;
188
+ }
189
+
190
+ const id = ident(expectedNode.value);
191
+
192
+ if (id) {
193
+ results[id] = node;
194
+
195
+ return results;
196
+ }
197
+
198
+ for (const key of keys(expectedNode.value)) {
199
+ if ([ 'loc', 'start', 'end' ].includes(key)) {
200
+ continue;
201
+ }
202
+
203
+ if (!hasProperty(node.value, key)) {
204
+ DBG && console.log('BAIL no property', key);
205
+ return;
206
+ }
207
+
208
+ const expectedVal = expectedNode.get(key);
209
+ const nodeVal = node.get(key);
210
+
211
+ if (wildcard(expectedVal.value)) {
212
+ continue;
213
+ }
214
+
215
+ const id = anyIdent(expectedVal.value);
216
+
217
+ if (id) {
218
+ results[id] = nodeVal;
219
+ continue;
220
+ }
221
+
222
+ if (isArray(expectedVal.value)) {
223
+ if (
224
+ expectedVal.value.some((el, idx) => {
225
+ return !matchNode(nodeVal.get(idx), expectedVal.get(idx), results);
226
+ })
227
+ ) {
228
+ DBG && console.log('BAIL no array match', key);
229
+ return;
230
+ }
231
+ } else
232
+ if (isNode(expectedVal.value)) {
233
+ if (!matchNode(nodeVal, expectedVal, results)) {
234
+ DBG && console.log('BAIL no match', key);
235
+ return;
236
+ }
237
+ } else {
238
+ if (nodeVal.value !== expectedVal.value) {
239
+ DBG && console.log('BAIL no primitive match', key);
240
+ return;
241
+ }
242
+ }
243
+ }
244
+
245
+ return results;
246
+ }
247
+
248
+ return function match(nodes) {
249
+ const matches = [];
250
+
251
+ for (const key of keys(nodes.value)) {
252
+ const node = nodes.get(key);
253
+ const matched = matchNode(node, expr);
254
+
255
+ if (matched) {
256
+ matches.push([ node, ...matched.slice(1) ]);
257
+ }
258
+ }
259
+
260
+ return matches;
261
+ };
262
+ }
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "bio-dts",
3
+ "version": "0.1.0",
4
+ "description": "Generate sane and clean types from JavaScript sources",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "bin": "bin/bio-dts",
8
+ "scripts": {
9
+ "all": "npm run lint && npm run test && npm run test:dts",
10
+ "lint": "eslint .",
11
+ "test": "mocha test",
12
+ "test:dts": "node bin/bio-dts.js --outDir dist -r test/fixtures"
13
+ },
14
+ "author": "Nico Rehwaldt",
15
+ "license": "MIT",
16
+ "dependencies": {
17
+ "@babel/parser": "^7.21.2",
18
+ "recast": "^0.22.0",
19
+ "tiny-glob": "^0.2.9",
20
+ "typescript": "^4.9.5"
21
+ },
22
+ "devDependencies": {
23
+ "chai": "^4.3.7",
24
+ "eslint": "^8.36.0",
25
+ "eslint-plugin-bpmn-io": "^1.0.0",
26
+ "mocha": "^10.2.0"
27
+ },
28
+ "files": [
29
+ "bin",
30
+ "lib",
31
+ "index.js"
32
+ ]
33
+ }