@playcanvas/attribute-parser 1.3.1 → 1.3.3
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/package.json +1 -2
- package/rollup.config.js +28 -0
- package/src/index.js +9 -2
- package/src/parsers/attribute-parser.js +4 -75
- package/src/parsers/script-parser.js +8 -12
- package/src/utils/attribute-utils.js +2 -28
- package/src/utils/ts-utils.js +13 -70
- package/test/fixtures/empty.valid.js +0 -0
- package/test/tests/valid/empty.test.js +17 -0
package/package.json
CHANGED
package/rollup.config.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import commonjs from '@rollup/plugin-commonjs';
|
|
2
|
+
import resolve from '@rollup/plugin-node-resolve';
|
|
3
|
+
import swc from '@rollup/plugin-swc';
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
input: 'src/index.js',
|
|
7
|
+
treeshake: { moduleSideEffects: false },
|
|
8
|
+
// Mark 'typescript' as external, so it won’t get bundled
|
|
9
|
+
// external: ['typescript'],
|
|
10
|
+
output: {
|
|
11
|
+
dir: 'dist',
|
|
12
|
+
format: 'esm'
|
|
13
|
+
},
|
|
14
|
+
plugins: [
|
|
15
|
+
commonjs({
|
|
16
|
+
include: /node_modules/,
|
|
17
|
+
ignoreGlobal: true,
|
|
18
|
+
sourceMap: false
|
|
19
|
+
}),
|
|
20
|
+
resolve({
|
|
21
|
+
browser: true, // This instructs the plugin to resolve for the browser
|
|
22
|
+
preferBuiltins: false // Prefer the local versions of built-ins instead of Node.js versions
|
|
23
|
+
}),
|
|
24
|
+
swc({ swc: {
|
|
25
|
+
minify: true
|
|
26
|
+
} })
|
|
27
|
+
]
|
|
28
|
+
};
|
package/src/index.js
CHANGED
|
@@ -123,10 +123,18 @@ export class JSDocParser {
|
|
|
123
123
|
throw new Error('PlayCanvas Types must be supplied');
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
const esmScripts = new Map();
|
|
127
|
+
|
|
126
128
|
// Parse the source file and pc types
|
|
127
129
|
const sourceFile = this.program.getSourceFile(fileName);
|
|
128
130
|
|
|
129
|
-
|
|
131
|
+
// In a special edge case where the files contents are empty, the sourcefile will be undefined
|
|
132
|
+
// So we should early out
|
|
133
|
+
if (sourceFile === undefined) {
|
|
134
|
+
return esmScripts;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (sourceFile === null) {
|
|
130
138
|
throw new Error(`Source file ${fileName} not found`);
|
|
131
139
|
}
|
|
132
140
|
|
|
@@ -135,7 +143,6 @@ export class JSDocParser {
|
|
|
135
143
|
|
|
136
144
|
const esmScriptClass = pcTypes.statements.find(node => node.kind === ts.SyntaxKind.ClassDeclaration && node.name.text === 'Script')?.symbol;
|
|
137
145
|
|
|
138
|
-
const esmScripts = new Map();
|
|
139
146
|
// Check if the file exports a class that inherits from `Script`
|
|
140
147
|
nodes.forEach((node, name) => {
|
|
141
148
|
if (isAliasedClassDeclaration(node, typeChecker) && inheritsFrom(node, typeChecker, esmScriptClass)) {
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { TSDocConfiguration, TSDocTagDefinition, TSDocTagSyntaxKind, TextRange, TSDocParser } from '@microsoft/tsdoc';
|
|
2
1
|
import * as ts from 'typescript';
|
|
3
2
|
|
|
4
3
|
import { ParsingError } from './parsing-error.js';
|
|
5
4
|
import { hasTag } from '../utils/attribute-utils.js';
|
|
6
5
|
import { parseTag, validateTag } from '../utils/tag-utils.js';
|
|
7
|
-
import {
|
|
6
|
+
import { getLiteralValue, getType } from '../utils/ts-utils.js';
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* A class to parse JSDoc comments and extract attribute metadata.
|
|
@@ -25,44 +24,18 @@ export class AttributeParser {
|
|
|
25
24
|
this.program = env.languageService.getProgram();
|
|
26
25
|
this.typeChecker = this.program.getTypeChecker();
|
|
27
26
|
|
|
28
|
-
this.attributeTag = new TSDocTagDefinition({
|
|
29
|
-
tagName: '@attribute', syntaxKind: TSDocTagSyntaxKind.ModifierTag
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
this.typeTag = new TSDocTagDefinition({
|
|
33
|
-
tagName: '@type', syntaxKind: TSDocTagSyntaxKind.BlockTag
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
const conf = new TSDocConfiguration();
|
|
37
|
-
conf.validation.reportUnsupportedTags = false;
|
|
38
|
-
|
|
39
|
-
conf.addTagDefinition(this.attributeTag);
|
|
40
|
-
|
|
41
|
-
tags.forEach((_, tag) => {
|
|
42
|
-
const tagDefinition = new TSDocTagDefinition({
|
|
43
|
-
tagName: `@${tag}`, syntaxKind: TSDocTagSyntaxKind.BlockTag, allowMultiple: false
|
|
44
|
-
});
|
|
45
|
-
conf.addTagDefinition(tagDefinition);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
this.parser = new TSDocParser(conf);
|
|
49
27
|
}
|
|
50
28
|
|
|
51
29
|
/**
|
|
52
30
|
* Parses a JSdoc comment and extracts any associated metadata if the `@attribute`
|
|
53
31
|
* modifier is used. Returns null if this is not an attribute comment.
|
|
54
32
|
*
|
|
55
|
-
* @param {TextRange} comment - The comment to parse
|
|
56
33
|
* @param {ts.Node} node - The node to parse
|
|
57
34
|
* @param {ParsingError[]} errors - An array to store any parsing errors
|
|
58
35
|
* @param {boolean} requiresAttributeTag - Whether the comment must have an attribute tag
|
|
59
36
|
* @returns {*} - The extracted metadata or null if no metadata was found
|
|
60
37
|
*/
|
|
61
|
-
parseAttributeComment(
|
|
62
|
-
|
|
63
|
-
const parserContext = this.parser.parseRange(comment);
|
|
64
|
-
const docComment = parserContext.docComment;
|
|
65
|
-
const file = node.getSourceFile();
|
|
38
|
+
parseAttributeComment(node, errors = [], requiresAttributeTag = true) {
|
|
66
39
|
|
|
67
40
|
// Check if the comment has an attribute tag
|
|
68
41
|
if (!requiresAttributeTag || hasTag('attribute', node)) {
|
|
@@ -73,7 +46,7 @@ export class AttributeParser {
|
|
|
73
46
|
if (!attribute) return;
|
|
74
47
|
|
|
75
48
|
// Extract the description from the summary section
|
|
76
|
-
const description =
|
|
49
|
+
const description = node.jsDoc[0].comment;
|
|
77
50
|
if (description) {
|
|
78
51
|
attribute.description = description.split('.')[0];
|
|
79
52
|
}
|
|
@@ -96,6 +69,7 @@ export class AttributeParser {
|
|
|
96
69
|
attribute[tag.tagName.text] = value;
|
|
97
70
|
|
|
98
71
|
} catch (error) {
|
|
72
|
+
const file = node.getSourceFile();
|
|
99
73
|
const { line, character } = file.getLineAndCharacterOfPosition(tag.getStart());
|
|
100
74
|
const parseError = new ParsingError(tag, `Error (${line}, ${character}): Parsing Tag '@${tag.tagName.text} ${commentText}' - ${error.message}`);
|
|
101
75
|
errors.push(parseError);
|
|
@@ -107,51 +81,6 @@ export class AttributeParser {
|
|
|
107
81
|
}
|
|
108
82
|
}
|
|
109
83
|
|
|
110
|
-
/**
|
|
111
|
-
* Returns the leading block comments for an typescript AST node.
|
|
112
|
-
*
|
|
113
|
-
* @param {import('typescript').Node} node - The node to parse
|
|
114
|
-
* @param {string[]} errors - An array to store any parsing errors
|
|
115
|
-
* @returns {import('@microsoft/tsdoc').DocComment} - The extracted doc comment;
|
|
116
|
-
*/
|
|
117
|
-
getCommentsForNode(node, errors = []) {
|
|
118
|
-
const fullText = node.getSourceFile().getFullText();
|
|
119
|
-
const comments = getLeadingBlockCommentRanges(node, fullText);
|
|
120
|
-
|
|
121
|
-
if (comments.length === 0) return null;
|
|
122
|
-
|
|
123
|
-
const lastComment = comments[0];
|
|
124
|
-
const textRange = TextRange.fromStringRange(fullText, lastComment.pos, lastComment.end);
|
|
125
|
-
|
|
126
|
-
const parserContext = this.parser.parseRange(textRange);
|
|
127
|
-
const docComment = parserContext.docComment;
|
|
128
|
-
|
|
129
|
-
// Check for comment parsing errors and flag them
|
|
130
|
-
this.populateErrors(node, parserContext, errors);
|
|
131
|
-
|
|
132
|
-
return docComment;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Populates the errors array with any parsing errors found in the parser context.
|
|
137
|
-
*
|
|
138
|
-
* @param {import('typescript').Node} node - The node to parse
|
|
139
|
-
* @param {import('@microsoft/tsdoc').ParserContext} parserContext - The parser context to check for errors
|
|
140
|
-
* @param {string[]} errors - An array to store any parsing errors
|
|
141
|
-
*/
|
|
142
|
-
populateErrors(node, parserContext, errors) {
|
|
143
|
-
if (parserContext.log.messages.length > 0) {
|
|
144
|
-
const sourceFile = node.getSourceFile();
|
|
145
|
-
|
|
146
|
-
const formattedErrors = parserContext.log.messages.map((message) => {
|
|
147
|
-
const location = sourceFile.getLineAndCharacterOfPosition(message.textRange.pos);
|
|
148
|
-
return `${sourceFile.fileName}(${location.line + 1},${location.character + 1}): [TSDoc] ${message}`;
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
errors.push(...formattedErrors);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
84
|
/**
|
|
156
85
|
* Extract the public members and their values from a TypeScript node.
|
|
157
86
|
*
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { TextRange } from '@microsoft/tsdoc';
|
|
2
1
|
import * as ts from 'typescript';
|
|
3
2
|
|
|
4
3
|
import { AttributeParser } from './attribute-parser.js';
|
|
5
4
|
import { ParsingError } from './parsing-error.js';
|
|
6
5
|
import { hasTag } from '../utils/attribute-utils.js';
|
|
7
6
|
import { zipArrays } from '../utils/generic-utils.js';
|
|
8
|
-
import { flatMapAnyNodes,
|
|
7
|
+
import { flatMapAnyNodes, getJSDocTags, getLiteralValue, parseArrayLiteral, parseFloatNode } from '../utils/ts-utils.js';
|
|
9
8
|
|
|
10
9
|
/**
|
|
11
10
|
* @typedef {object} Attribute
|
|
@@ -322,17 +321,14 @@ export class ScriptParser {
|
|
|
322
321
|
}
|
|
323
322
|
|
|
324
323
|
// Find "/** */" style comments associated with this node.
|
|
325
|
-
const
|
|
324
|
+
const members = getJSDocTags(node, this.typeChecker);
|
|
326
325
|
|
|
327
326
|
// Parse the comments for attribute metadata
|
|
328
|
-
for (const
|
|
329
|
-
|
|
330
|
-
const memberFileText = comment.member.getSourceFile().getFullText();
|
|
327
|
+
for (const { member, memberName } of members) {
|
|
331
328
|
|
|
332
329
|
// Parse the comment for attribute metadata
|
|
333
330
|
const attributeMetadata = this.attributeParser.parseAttributeComment(
|
|
334
|
-
|
|
335
|
-
comment.member,
|
|
331
|
+
member,
|
|
336
332
|
errors,
|
|
337
333
|
requiresAttributeTag
|
|
338
334
|
);
|
|
@@ -341,11 +337,11 @@ export class ScriptParser {
|
|
|
341
337
|
if (attributeMetadata) {
|
|
342
338
|
|
|
343
339
|
const { type, typeName } = attributeMetadata;
|
|
344
|
-
const initializer =
|
|
340
|
+
const initializer = member?.initializer;
|
|
345
341
|
|
|
346
342
|
if (typeName === 'any') {
|
|
347
343
|
|
|
348
|
-
const error = new ParsingError(
|
|
344
|
+
const error = new ParsingError(member, `The attribute "${memberName}" is not initialized and does not have a @type tag.`);
|
|
349
345
|
errors.push(error);
|
|
350
346
|
continue;
|
|
351
347
|
|
|
@@ -369,7 +365,7 @@ export class ScriptParser {
|
|
|
369
365
|
|
|
370
366
|
// Check if the type is a valid interface
|
|
371
367
|
if (!isInitialized && !typeIsInterface && !typeIsJSDocTypeDef && !isJSDocTypeLiteral) {
|
|
372
|
-
const error = new ParsingError(
|
|
368
|
+
const error = new ParsingError(member, `Attribute "${memberName}" is an invalid type.`);
|
|
373
369
|
errors.push(error);
|
|
374
370
|
continue;
|
|
375
371
|
}
|
|
@@ -408,7 +404,7 @@ export class ScriptParser {
|
|
|
408
404
|
}
|
|
409
405
|
|
|
410
406
|
// Add the attribute to the list of found attributes
|
|
411
|
-
attributes[
|
|
407
|
+
attributes[memberName] = mapAttributesToOutput(attributeMetadata);
|
|
412
408
|
|
|
413
409
|
}
|
|
414
410
|
}
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Returns a
|
|
7
|
+
* Returns a jsdoc tag from a JSDoc comment.
|
|
8
8
|
* @param {string} tag - The tag to search for
|
|
9
9
|
* @param {import('typescript').Node} doc - The JSDoc comment node
|
|
10
|
-
* @returns {import('
|
|
10
|
+
* @returns {import('typescript').Node} | null} - The tag node
|
|
11
11
|
*/
|
|
12
12
|
export const getTagFromJsdoc = (tag, doc) => {
|
|
13
13
|
return doc?.tags?.find(doc => doc.tagName.text === tag);
|
|
@@ -42,29 +42,3 @@ export const hasTag = (tag, node) => {
|
|
|
42
42
|
export const isInterface = (node) => {
|
|
43
43
|
return hasTag('interface', node);
|
|
44
44
|
};
|
|
45
|
-
|
|
46
|
-
// /**
|
|
47
|
-
// * Parses a type tag in the format "{type}".
|
|
48
|
-
// * @param {import('@microsoft/tsdoc').DocSection} content - The content of the tag.
|
|
49
|
-
// * @param {object} metadata - An object to store the extracted metadata.
|
|
50
|
-
// * @param {{ node: import('typescript').Node, env: tsdoc.Environment, errors: string[] }} opts - Additional parser options
|
|
51
|
-
// * @returns {{ type: import('typescript').Type | null, array: boolean | null }} - The extracted type and array flag.
|
|
52
|
-
// */
|
|
53
|
-
// export function parseTypeTag(content, metadata, { node, env, errors }) {
|
|
54
|
-
|
|
55
|
-
// // Extract the full source text of the original file
|
|
56
|
-
// const originalSourceText = node.getSourceFile().getFullText();
|
|
57
|
-
// const typeString = extractTextFromDocNode(content);
|
|
58
|
-
// const sourceText = `${originalSourceText}\nlet a: ${typeString};`;
|
|
59
|
-
|
|
60
|
-
// env.createFile("/virtual.ts", sourceText);
|
|
61
|
-
|
|
62
|
-
// const updatedProgram = env.languageService.getProgram();
|
|
63
|
-
// const newTypeChecker = updatedProgram.getTypeChecker();
|
|
64
|
-
|
|
65
|
-
// const sourceFile = updatedProgram.getSourceFile('/virtual.ts');
|
|
66
|
-
|
|
67
|
-
// const variableStatement = sourceFile.statements[sourceFile.statements.length - 1];
|
|
68
|
-
// const variableDeclaration = variableStatement.declarationList.declarations[0];
|
|
69
|
-
// return getType(variableDeclaration, newTypeChecker);
|
|
70
|
-
// }
|
package/src/utils/ts-utils.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { DocNodeKind } from '@microsoft/tsdoc';
|
|
2
1
|
import { knownLibFilesForCompilerOptions } from '@typescript/vfs';
|
|
3
2
|
import * as ts from 'typescript';
|
|
4
3
|
|
|
@@ -205,10 +204,10 @@ function getSuperClasses(node, typeChecker) {
|
|
|
205
204
|
* @param {import('typescript').Node} node - The TypeScript node to analyze
|
|
206
205
|
* @param {string} text - The full text content of the source file
|
|
207
206
|
* @param {import('typescript').TypeChecker} typeChecker - The TypeScript type checker
|
|
208
|
-
* @returns {{ memberName: string, member:
|
|
207
|
+
* @returns {{ memberName: string, member: Node, range: import('typescript').CommentRange}[]} - An array of comment ranges
|
|
209
208
|
*/
|
|
210
|
-
export function
|
|
211
|
-
const
|
|
209
|
+
export function getJSDocTags(node, typeChecker) {
|
|
210
|
+
const tags = [];
|
|
212
211
|
|
|
213
212
|
if (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) {
|
|
214
213
|
// get an array of the class an all parent classed
|
|
@@ -221,12 +220,11 @@ export function getJSDocCommentRanges(node, typeChecker) {
|
|
|
221
220
|
classNode.members.forEach((member) => {
|
|
222
221
|
if (ts.isPropertyDeclaration(member) || ts.isSetAccessor(member) || ts.isPropertySignature(member)) {
|
|
223
222
|
const memberName = member.name && ts.isIdentifier(member.name) ? member.name.text : 'unnamed';
|
|
224
|
-
const
|
|
225
|
-
if (
|
|
226
|
-
|
|
223
|
+
const hasJsDoc = member.jsDoc?.length > 0;
|
|
224
|
+
if (hasJsDoc) {
|
|
225
|
+
tags.push({
|
|
227
226
|
memberName,
|
|
228
|
-
member
|
|
229
|
-
range: ranges[0]
|
|
227
|
+
member
|
|
230
228
|
});
|
|
231
229
|
}
|
|
232
230
|
}
|
|
@@ -234,62 +232,7 @@ export function getJSDocCommentRanges(node, typeChecker) {
|
|
|
234
232
|
});
|
|
235
233
|
}
|
|
236
234
|
|
|
237
|
-
return
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
const ASTERISK = 42;
|
|
241
|
-
const SLASH = 47;
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Retrieves the JSDoc-style comments blocks associated with a specific AST node.
|
|
245
|
-
* @param {import('typescript').Node} node - The TypeScript node to analyze
|
|
246
|
-
* @param {string} text - The full text content of the source file
|
|
247
|
-
* @returns {import('typescript').CommentRange[]} - An array of comment ranges
|
|
248
|
-
*/
|
|
249
|
-
export function getLeadingBlockCommentRanges(node, text) {
|
|
250
|
-
const ranges = ts.getLeadingCommentRanges(text, node.getFullStart()) ?? [];
|
|
251
|
-
return ranges.filter(({ pos }) => {
|
|
252
|
-
return (
|
|
253
|
-
text.charCodeAt(pos + 1) === ASTERISK &&
|
|
254
|
-
text.charCodeAt(pos + 2) === ASTERISK &&
|
|
255
|
-
text.charCodeAt(pos + 3) !== SLASH
|
|
256
|
-
);
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Recursively extract text content from a DocNode.
|
|
262
|
-
* @param {tsdoc.DocNode} node - The DocNode from which to extract text.
|
|
263
|
-
* @returns {string} - The concatenated string of all text content.
|
|
264
|
-
*/
|
|
265
|
-
export function extractTextFromDocNode(node) {
|
|
266
|
-
// Check if the current node is a PlainText node
|
|
267
|
-
if (node.kind === DocNodeKind.PlainText) {
|
|
268
|
-
return node.text.trim(); // Cast and return as we found a PlainText node
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// If not, recursively search its children
|
|
272
|
-
for (const child of node.getChildNodes()) {
|
|
273
|
-
const result = extractTextFromDocNode(child);
|
|
274
|
-
if (result !== null) {
|
|
275
|
-
return result; // Return the first PlainText node found in the subtree
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
return null;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
* Extracts all imports from a TypeScript source file.
|
|
284
|
-
* @param {ts.Node} node - The TypeScript node to analyze
|
|
285
|
-
* @returns {string[]} - An array of import statements
|
|
286
|
-
*/
|
|
287
|
-
export function getLeadingComments(node) {
|
|
288
|
-
const fullText = node.getFullText();
|
|
289
|
-
const commentRanges = ts.getLeadingCommentRanges(fullText, 0);
|
|
290
|
-
|
|
291
|
-
if (!commentRanges) return [];
|
|
292
|
-
return commentRanges.map(range => fullText.slice(range.pos, range.end));
|
|
235
|
+
return tags;
|
|
293
236
|
}
|
|
294
237
|
|
|
295
238
|
/**
|
|
@@ -343,7 +286,7 @@ export function getPrimitiveEnumType(type, typeChecker) {
|
|
|
343
286
|
*
|
|
344
287
|
* @param {ts.Node} node - The TypeScript node to analyze
|
|
345
288
|
* @param {ts.TypeChecker} typeChecker - The TypeScript type checker
|
|
346
|
-
* @returns {{ array: boolean, name: string | undefined, type:
|
|
289
|
+
* @returns {{ array: boolean, name: string | undefined, type: Type | null }} - The inferred type of the node
|
|
347
290
|
*/
|
|
348
291
|
export function getType(node, typeChecker) {
|
|
349
292
|
if (node?.name?.kind === ts.SyntaxKind.Identifier) {
|
|
@@ -565,11 +508,11 @@ const evaluatePrefixUnaryExpression = (node, typeChecker) => {
|
|
|
565
508
|
function handleObjectLiteral(node, typeChecker) {
|
|
566
509
|
const obj = {};
|
|
567
510
|
node.properties.forEach((prop) => {
|
|
568
|
-
if
|
|
511
|
+
if (ts.isPropertyAssignment(prop)) {
|
|
569
512
|
const key = prop.name.getText();
|
|
570
513
|
const value = getLiteralValue(prop.initializer, typeChecker);
|
|
571
514
|
obj[key] = value;
|
|
572
|
-
} else if
|
|
515
|
+
} else if (ts.isShorthandPropertyAssignment(prop)) {
|
|
573
516
|
const key = prop.name.getText();
|
|
574
517
|
const value = resolveIdentifier(prop.name, typeChecker);
|
|
575
518
|
obj[key] = value;
|
|
@@ -632,7 +575,7 @@ export function getLiteralValue(node, typeChecker) {
|
|
|
632
575
|
*/
|
|
633
576
|
export const parseStringNode = (node) => {
|
|
634
577
|
if (node.kind !== ts.SyntaxKind.StringLiteral) {
|
|
635
|
-
if
|
|
578
|
+
if (ts.isPropertyAssignment(node.parent) || ts.isPropertyDeclaration(node.parent)) {
|
|
636
579
|
throw new ParsingError(node, `"${node.parent.getText()}" must be defined with a string literal.`);
|
|
637
580
|
}
|
|
638
581
|
return '';
|
|
@@ -642,7 +585,7 @@ export const parseStringNode = (node) => {
|
|
|
642
585
|
|
|
643
586
|
export const parseArrayLiteralNode = (node, isArgument = false) => {
|
|
644
587
|
if (node.kind !== ts.SyntaxKind.ArrayLiteralExpression) {
|
|
645
|
-
if
|
|
588
|
+
if (ts.isPropertyAssignment(node.parent) || ts.isPropertyDeclaration(node.parent) || isArgument) {
|
|
646
589
|
throw new ParsingError(node, `"${node.parent.getText()}" must be defined with an array literal.`);
|
|
647
590
|
}
|
|
648
591
|
return [];
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { expect } from 'chai';
|
|
2
|
+
import { describe, it, before } from 'mocha';
|
|
3
|
+
|
|
4
|
+
import { parseAttributes } from '../../utils.js';
|
|
5
|
+
|
|
6
|
+
describe('VALID: Program ', function () {
|
|
7
|
+
let data;
|
|
8
|
+
before(async function () {
|
|
9
|
+
data = await parseAttributes('./empty.valid.js');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('only results should exist', function () {
|
|
13
|
+
expect(data).to.exist;
|
|
14
|
+
expect(data[0]).to.be.empty;
|
|
15
|
+
expect(data[1]).to.be.empty;
|
|
16
|
+
});
|
|
17
|
+
});
|