@unrdf/kgn 5.0.1
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 +21 -0
- package/README.md +210 -0
- package/package.json +90 -0
- package/src/MIGRATION_COMPLETE.md +186 -0
- package/src/PORT-MAP.md +302 -0
- package/src/base/filter-templates.js +479 -0
- package/src/base/index.js +92 -0
- package/src/base/injection-targets.js +583 -0
- package/src/base/macro-templates.js +298 -0
- package/src/base/macro-templates.js.bak +461 -0
- package/src/base/shacl-templates.js +617 -0
- package/src/base/template-base.js +388 -0
- package/src/core/attestor.js +381 -0
- package/src/core/filters.js +518 -0
- package/src/core/index.js +21 -0
- package/src/core/kgen-engine.js +372 -0
- package/src/core/parser.js +447 -0
- package/src/core/post-processor.js +313 -0
- package/src/core/renderer.js +469 -0
- package/src/doc-generator/cli.mjs +122 -0
- package/src/doc-generator/index.mjs +28 -0
- package/src/doc-generator/mdx-generator.mjs +71 -0
- package/src/doc-generator/nav-generator.mjs +136 -0
- package/src/doc-generator/parser.mjs +291 -0
- package/src/doc-generator/rdf-builder.mjs +306 -0
- package/src/doc-generator/scanner.mjs +189 -0
- package/src/engine/index.js +42 -0
- package/src/engine/pipeline.js +448 -0
- package/src/engine/renderer.js +604 -0
- package/src/engine/template-engine.js +566 -0
- package/src/filters/array.js +436 -0
- package/src/filters/data.js +479 -0
- package/src/filters/index.js +270 -0
- package/src/filters/rdf.js +264 -0
- package/src/filters/text.js +369 -0
- package/src/index.js +109 -0
- package/src/inheritance/index.js +40 -0
- package/src/injection/api.js +260 -0
- package/src/injection/atomic-writer.js +327 -0
- package/src/injection/constants.js +136 -0
- package/src/injection/idempotency-manager.js +295 -0
- package/src/injection/index.js +28 -0
- package/src/injection/injection-engine.js +378 -0
- package/src/injection/integration.js +339 -0
- package/src/injection/modes/index.js +341 -0
- package/src/injection/rollback-manager.js +373 -0
- package/src/injection/target-resolver.js +323 -0
- package/src/injection/tests/atomic-writer.test.js +382 -0
- package/src/injection/tests/injection-engine.test.js +611 -0
- package/src/injection/tests/integration.test.js +392 -0
- package/src/injection/tests/run-tests.js +283 -0
- package/src/injection/validation-engine.js +547 -0
- package/src/linter/determinism-linter.js +473 -0
- package/src/linter/determinism.js +410 -0
- package/src/linter/index.js +6 -0
- package/src/linter/test-doubles.js +475 -0
- package/src/parser/frontmatter.js +228 -0
- package/src/parser/variables.js +344 -0
- package/src/renderer/deterministic.js +245 -0
- package/src/renderer/index.js +6 -0
- package/src/templates/latex/academic-paper.njk +186 -0
- package/src/templates/latex/index.js +104 -0
- package/src/templates/nextjs/app-page.njk +66 -0
- package/src/templates/nextjs/index.js +80 -0
- package/src/templates/office/docx/document.njk +368 -0
- package/src/templates/office/index.js +79 -0
- package/src/templates/office/word-report.njk +129 -0
- package/src/utils/template-utils.js +426 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Navigation generator for Nextra _meta.json files
|
|
3
|
+
* Creates sidebar navigation from scanned packages
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Generate _meta.json for Nextra navigation
|
|
8
|
+
* @param {Object} groupedFiles - Files grouped by package/module
|
|
9
|
+
* @param {Object} options - Generation options
|
|
10
|
+
* @returns {Object} Nextra _meta.json structure
|
|
11
|
+
*/
|
|
12
|
+
export function generateNavigation(groupedFiles, options = {}) {
|
|
13
|
+
const {
|
|
14
|
+
rootTitle = 'API Reference',
|
|
15
|
+
includeIndex = true,
|
|
16
|
+
} = options;
|
|
17
|
+
|
|
18
|
+
const meta = {
|
|
19
|
+
index: includeIndex ? 'Overview' : undefined,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// Add each package to navigation
|
|
23
|
+
Object.entries(groupedFiles).forEach(([pkgName, pkgData]) => {
|
|
24
|
+
const pkgKey = `api/${pkgName}`;
|
|
25
|
+
|
|
26
|
+
// Package entry
|
|
27
|
+
meta[pkgKey] = {
|
|
28
|
+
title: `@unrdf/${pkgName}`,
|
|
29
|
+
type: 'page',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Add modules within package
|
|
33
|
+
Object.keys(pkgData.modules).forEach(moduleName => {
|
|
34
|
+
if (moduleName === 'root') {
|
|
35
|
+
// Files in root of src/ - add directly under package
|
|
36
|
+
meta[`${pkgKey}/index`] = `${pkgName} API`;
|
|
37
|
+
} else {
|
|
38
|
+
// Submodule
|
|
39
|
+
meta[`${pkgKey}/${moduleName}`] = {
|
|
40
|
+
title: moduleName.charAt(0).toUpperCase() + moduleName.slice(1),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
return meta;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Generate package index page content
|
|
51
|
+
* @param {string} packageName - Package name
|
|
52
|
+
* @param {Object} packageData - Package data from scanner
|
|
53
|
+
* @returns {string} MDX content for package index
|
|
54
|
+
*/
|
|
55
|
+
export function generatePackageIndex(packageName, packageData) {
|
|
56
|
+
const { modules, relativePath } = packageData;
|
|
57
|
+
const moduleNames = Object.keys(modules);
|
|
58
|
+
|
|
59
|
+
return `# @unrdf/${packageName}
|
|
60
|
+
|
|
61
|
+
> Package: \`${relativePath}\`
|
|
62
|
+
|
|
63
|
+
## Modules
|
|
64
|
+
|
|
65
|
+
This package contains ${moduleNames.length} module${moduleNames.length !== 1 ? 's' : ''}:
|
|
66
|
+
|
|
67
|
+
${moduleNames.map(name => {
|
|
68
|
+
const files = modules[name];
|
|
69
|
+
return `### ${name === 'root' ? 'Core' : name.charAt(0).toUpperCase() + name.slice(1)}
|
|
70
|
+
|
|
71
|
+
${files.length} source file${files.length !== 1 ? 's' : ''}
|
|
72
|
+
|
|
73
|
+
${files.slice(0, 5).map(f => {
|
|
74
|
+
const fileName = f.split('/').pop();
|
|
75
|
+
const moduleLink = name === 'root' ? fileName.replace(/\.(m?js)$/, '') : `${name}/${fileName.replace(/\.(m?js)$/, '')}`;
|
|
76
|
+
return `- [\`${fileName}\`](./${moduleLink})`;
|
|
77
|
+
}).join('\n')}
|
|
78
|
+
|
|
79
|
+
${files.length > 5 ? `\n...and ${files.length - 5} more` : ''}
|
|
80
|
+
`;
|
|
81
|
+
}).join('\n')}
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
*Generated by @unrdf/kgn documentation generator*
|
|
86
|
+
`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Generate root API index page
|
|
91
|
+
* @param {Object} groupedFiles - All packages and modules
|
|
92
|
+
* @returns {string} MDX content for API index
|
|
93
|
+
*/
|
|
94
|
+
export function generateAPIIndex(groupedFiles) {
|
|
95
|
+
const packages = Object.entries(groupedFiles);
|
|
96
|
+
|
|
97
|
+
return `# API Reference
|
|
98
|
+
|
|
99
|
+
Welcome to the @unrdf workspace API reference.
|
|
100
|
+
|
|
101
|
+
## Packages
|
|
102
|
+
|
|
103
|
+
The workspace contains ${packages.length} package${packages.length !== 1 ? 's' : ''}:
|
|
104
|
+
|
|
105
|
+
${packages.map(([pkgName, pkgData]) => {
|
|
106
|
+
const moduleCount = Object.keys(pkgData.modules).length;
|
|
107
|
+
const fileCount = pkgData.sourceFiles.length;
|
|
108
|
+
|
|
109
|
+
return `### [@unrdf/${pkgName}](./${pkgName})
|
|
110
|
+
|
|
111
|
+
${moduleCount} module${moduleCount !== 1 ? 's' : ''}, ${fileCount} source file${fileCount !== 1 ? 's' : ''}
|
|
112
|
+
|
|
113
|
+
${pkgData.modules ? Object.keys(pkgData.modules).slice(0, 3).map(m =>
|
|
114
|
+
`- ${m === 'root' ? 'Core' : m}`
|
|
115
|
+
).join('\n') : ''}
|
|
116
|
+
${moduleCount > 3 ? `- ...and ${moduleCount - 3} more` : ''}
|
|
117
|
+
`;
|
|
118
|
+
}).join('\n')}
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Search
|
|
123
|
+
|
|
124
|
+
Use the search bar above to find specific functions, classes, or modules.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
*Documentation generated from JSDoc comments using @unrdf/kgn*
|
|
129
|
+
`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export default {
|
|
133
|
+
generateNavigation,
|
|
134
|
+
generatePackageIndex,
|
|
135
|
+
generateAPIIndex,
|
|
136
|
+
};
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview JSDoc Parser with Babel AST Analysis
|
|
3
|
+
* Extracts documentation from JavaScript/TypeScript source files
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { parse as parseComments } from 'comment-parser';
|
|
7
|
+
import { parse as parseBabel } from '@babel/parser';
|
|
8
|
+
import traverse from '@babel/traverse';
|
|
9
|
+
import { readFileSync } from 'fs';
|
|
10
|
+
import { relative } from 'path';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Parse JSDoc comments and AST from a source file
|
|
14
|
+
* @param {string} filePath - Absolute path to source file
|
|
15
|
+
* @param {string} rootDir - Workspace root directory
|
|
16
|
+
* @returns {Object} Parsed documentation data
|
|
17
|
+
*/
|
|
18
|
+
export function parseFile(filePath, rootDir = process.cwd()) {
|
|
19
|
+
const code = readFileSync(filePath, 'utf-8');
|
|
20
|
+
const relativePath = relative(rootDir, filePath);
|
|
21
|
+
|
|
22
|
+
// Parse JSDoc comments
|
|
23
|
+
const comments = extractJSDocComments(code);
|
|
24
|
+
|
|
25
|
+
// Parse AST
|
|
26
|
+
const ast = parseBabel(code, {
|
|
27
|
+
sourceType: 'module',
|
|
28
|
+
plugins: ['jsx', 'typescript'],
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Extract exports and their metadata
|
|
32
|
+
const exports = extractExports(ast, comments);
|
|
33
|
+
|
|
34
|
+
// Extract imports for cross-referencing
|
|
35
|
+
const imports = extractImports(ast);
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
file: filePath,
|
|
39
|
+
relativePath,
|
|
40
|
+
exports,
|
|
41
|
+
imports,
|
|
42
|
+
comments: comments.length,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Extract JSDoc comments from source code
|
|
48
|
+
* @param {string} code - Source code
|
|
49
|
+
* @returns {Array} Parsed JSDoc blocks
|
|
50
|
+
*/
|
|
51
|
+
function extractJSDocComments(code) {
|
|
52
|
+
// Extract all comments matching JSDoc pattern
|
|
53
|
+
const jsDocRegex = /\/\*\*[\s\S]*?\*\//g;
|
|
54
|
+
const matches = code.match(jsDocRegex) || [];
|
|
55
|
+
|
|
56
|
+
return matches.map(block => {
|
|
57
|
+
const parsed = parseComments(block)[0];
|
|
58
|
+
return {
|
|
59
|
+
description: parsed.description,
|
|
60
|
+
tags: parsed.tags.map(tag => ({
|
|
61
|
+
tag: tag.tag,
|
|
62
|
+
name: tag.name,
|
|
63
|
+
type: tag.type,
|
|
64
|
+
description: tag.description,
|
|
65
|
+
optional: tag.optional,
|
|
66
|
+
default: tag.default,
|
|
67
|
+
})),
|
|
68
|
+
source: block,
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Extract exports from AST
|
|
75
|
+
* @param {Object} ast - Babel AST
|
|
76
|
+
* @param {Array} comments - Parsed JSDoc comments
|
|
77
|
+
* @returns {Array} Export declarations with metadata
|
|
78
|
+
*/
|
|
79
|
+
function extractExports(ast, comments) {
|
|
80
|
+
const exports = [];
|
|
81
|
+
let commentIndex = 0;
|
|
82
|
+
|
|
83
|
+
traverse.default(ast, {
|
|
84
|
+
ExportNamedDeclaration(path) {
|
|
85
|
+
const { declaration } = path.node;
|
|
86
|
+
|
|
87
|
+
if (!declaration) return;
|
|
88
|
+
|
|
89
|
+
// Find associated JSDoc comment
|
|
90
|
+
const jsdoc = commentIndex < comments.length ? comments[commentIndex++] : null;
|
|
91
|
+
|
|
92
|
+
if (declaration.type === 'FunctionDeclaration') {
|
|
93
|
+
exports.push(extractFunctionExport(declaration, jsdoc));
|
|
94
|
+
} else if (declaration.type === 'VariableDeclaration') {
|
|
95
|
+
declaration.declarations.forEach(decl => {
|
|
96
|
+
if (decl.init && (decl.init.type === 'FunctionExpression' || decl.init.type === 'ArrowFunctionExpression')) {
|
|
97
|
+
exports.push(extractFunctionExport(decl, jsdoc));
|
|
98
|
+
} else {
|
|
99
|
+
exports.push(extractVariableExport(decl, jsdoc));
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
} else if (declaration.type === 'ClassDeclaration') {
|
|
103
|
+
exports.push(extractClassExport(declaration, jsdoc));
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
ExportDefaultDeclaration(path) {
|
|
108
|
+
const { declaration } = path.node;
|
|
109
|
+
const jsdoc = commentIndex < comments.length ? comments[commentIndex++] : null;
|
|
110
|
+
|
|
111
|
+
exports.push({
|
|
112
|
+
name: 'default',
|
|
113
|
+
type: declaration.type,
|
|
114
|
+
isDefault: true,
|
|
115
|
+
jsdoc,
|
|
116
|
+
});
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
return exports;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Extract function export metadata
|
|
125
|
+
* @param {Object} node - AST node
|
|
126
|
+
* @param {Object} jsdoc - JSDoc comment
|
|
127
|
+
* @returns {Object} Function metadata
|
|
128
|
+
*/
|
|
129
|
+
function extractFunctionExport(node, jsdoc) {
|
|
130
|
+
const name = node.id?.name || node.key?.name;
|
|
131
|
+
const params = extractParams(node.params || node.init?.params || []);
|
|
132
|
+
|
|
133
|
+
// Extract metadata from JSDoc
|
|
134
|
+
const paramDocs = jsdoc?.tags.filter(t => t.tag === 'param') || [];
|
|
135
|
+
const returnDoc = jsdoc?.tags.find(t => t.tag === 'returns' || t.tag === 'return');
|
|
136
|
+
const examples = jsdoc?.tags.filter(t => t.tag === 'example').map(t => t.description) || [];
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
name,
|
|
140
|
+
type: 'function',
|
|
141
|
+
description: jsdoc?.description || '',
|
|
142
|
+
params: params.map((param, i) => ({
|
|
143
|
+
name: param.name,
|
|
144
|
+
type: paramDocs[i]?.type || 'any',
|
|
145
|
+
description: paramDocs[i]?.description || '',
|
|
146
|
+
optional: param.optional || paramDocs[i]?.optional || false,
|
|
147
|
+
default: param.default || paramDocs[i]?.default,
|
|
148
|
+
})),
|
|
149
|
+
returns: {
|
|
150
|
+
type: returnDoc?.type || 'void',
|
|
151
|
+
description: returnDoc?.description || '',
|
|
152
|
+
},
|
|
153
|
+
examples,
|
|
154
|
+
async: node.async || node.init?.async || false,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Extract variable export metadata
|
|
160
|
+
* @param {Object} node - AST node
|
|
161
|
+
* @param {Object} jsdoc - JSDoc comment
|
|
162
|
+
* @returns {Object} Variable metadata
|
|
163
|
+
*/
|
|
164
|
+
function extractVariableExport(node, jsdoc) {
|
|
165
|
+
return {
|
|
166
|
+
name: node.id?.name,
|
|
167
|
+
type: 'variable',
|
|
168
|
+
description: jsdoc?.description || '',
|
|
169
|
+
valueType: node.init?.type || 'unknown',
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Extract class export metadata
|
|
175
|
+
* @param {Object} node - AST node
|
|
176
|
+
* @param {Object} jsdoc - JSDoc comment
|
|
177
|
+
* @returns {Object} Class metadata
|
|
178
|
+
*/
|
|
179
|
+
function extractClassExport(node, jsdoc) {
|
|
180
|
+
const methods = [];
|
|
181
|
+
|
|
182
|
+
node.body.body.forEach(member => {
|
|
183
|
+
if (member.type === 'ClassMethod') {
|
|
184
|
+
methods.push({
|
|
185
|
+
name: member.key.name,
|
|
186
|
+
kind: member.kind, // 'constructor', 'method', 'get', 'set'
|
|
187
|
+
static: member.static,
|
|
188
|
+
async: member.async,
|
|
189
|
+
params: extractParams(member.params),
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
name: node.id.name,
|
|
196
|
+
type: 'class',
|
|
197
|
+
description: jsdoc?.description || '',
|
|
198
|
+
methods,
|
|
199
|
+
extends: node.superClass?.name,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Extract parameter metadata from AST nodes
|
|
205
|
+
* @param {Array} params - Parameter AST nodes
|
|
206
|
+
* @returns {Array} Parameter metadata
|
|
207
|
+
*/
|
|
208
|
+
function extractParams(params) {
|
|
209
|
+
return params.map(param => {
|
|
210
|
+
if (param.type === 'Identifier') {
|
|
211
|
+
return { name: param.name, optional: false };
|
|
212
|
+
} else if (param.type === 'AssignmentPattern') {
|
|
213
|
+
return {
|
|
214
|
+
name: param.left.name,
|
|
215
|
+
optional: true,
|
|
216
|
+
default: extractDefaultValue(param.right),
|
|
217
|
+
};
|
|
218
|
+
} else if (param.type === 'RestElement') {
|
|
219
|
+
return { name: param.argument.name, rest: true };
|
|
220
|
+
} else if (param.type === 'ObjectPattern') {
|
|
221
|
+
return { name: 'options', destructured: true };
|
|
222
|
+
}
|
|
223
|
+
return { name: 'unknown' };
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Extract default value from AST node
|
|
229
|
+
* @param {Object} node - AST node
|
|
230
|
+
* @returns {any} Default value
|
|
231
|
+
*/
|
|
232
|
+
function extractDefaultValue(node) {
|
|
233
|
+
if (node.type === 'Literal') return node.value;
|
|
234
|
+
if (node.type === 'NumericLiteral') return node.value;
|
|
235
|
+
if (node.type === 'StringLiteral') return node.value;
|
|
236
|
+
if (node.type === 'BooleanLiteral') return node.value;
|
|
237
|
+
if (node.type === 'NullLiteral') return null;
|
|
238
|
+
if (node.type === 'ObjectExpression') return {};
|
|
239
|
+
if (node.type === 'ArrayExpression') return [];
|
|
240
|
+
return undefined;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Extract import statements for cross-referencing
|
|
245
|
+
* @param {Object} ast - Babel AST
|
|
246
|
+
* @returns {Array} Import declarations
|
|
247
|
+
*/
|
|
248
|
+
function extractImports(ast) {
|
|
249
|
+
const imports = [];
|
|
250
|
+
|
|
251
|
+
traverse.default(ast, {
|
|
252
|
+
ImportDeclaration(path) {
|
|
253
|
+
const { source, specifiers } = path.node;
|
|
254
|
+
|
|
255
|
+
imports.push({
|
|
256
|
+
source: source.value,
|
|
257
|
+
specifiers: specifiers.map(spec => ({
|
|
258
|
+
imported: spec.imported?.name || spec.local.name,
|
|
259
|
+
local: spec.local.name,
|
|
260
|
+
type: spec.type, // 'ImportSpecifier', 'ImportDefaultSpecifier', 'ImportNamespaceSpecifier'
|
|
261
|
+
})),
|
|
262
|
+
});
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
return imports;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Parse multiple files in batch
|
|
271
|
+
* @param {Array<string>} filePaths - Array of file paths
|
|
272
|
+
* @param {string} rootDir - Workspace root directory
|
|
273
|
+
* @returns {Array} Parsed data for all files
|
|
274
|
+
*/
|
|
275
|
+
export function parseFiles(filePaths, rootDir = process.cwd()) {
|
|
276
|
+
return filePaths.map(filePath => {
|
|
277
|
+
try {
|
|
278
|
+
return parseFile(filePath, rootDir);
|
|
279
|
+
} catch (error) {
|
|
280
|
+
console.error(`Failed to parse ${filePath}:`, error.message);
|
|
281
|
+
return {
|
|
282
|
+
file: filePath,
|
|
283
|
+
error: error.message,
|
|
284
|
+
exports: [],
|
|
285
|
+
imports: [],
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export default { parseFile, parseFiles };
|