@jrc03c/gtlint 0.12.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.
Files changed (152) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +235 -0
  3. package/bin/gtlint.js +4 -0
  4. package/dist/cli.d.ts +3 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +248 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/config.d.ts +32 -0
  9. package/dist/config.d.ts.map +1 -0
  10. package/dist/config.js +112 -0
  11. package/dist/config.js.map +1 -0
  12. package/dist/formatter/formatter.d.ts +22 -0
  13. package/dist/formatter/formatter.d.ts.map +1 -0
  14. package/dist/formatter/formatter.js +376 -0
  15. package/dist/formatter/formatter.js.map +1 -0
  16. package/dist/formatter/index.d.ts +2 -0
  17. package/dist/formatter/index.d.ts.map +1 -0
  18. package/dist/formatter/index.js +2 -0
  19. package/dist/formatter/index.js.map +1 -0
  20. package/dist/index.d.ts +11 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +12 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/language/index.d.ts +8 -0
  25. package/dist/language/index.d.ts.map +1 -0
  26. package/dist/language/index.js +8 -0
  27. package/dist/language/index.js.map +1 -0
  28. package/dist/language/keyword-spec.d.ts +87 -0
  29. package/dist/language/keyword-spec.d.ts.map +1 -0
  30. package/dist/language/keyword-spec.js +772 -0
  31. package/dist/language/keyword-spec.js.map +1 -0
  32. package/dist/lexer/index.d.ts +3 -0
  33. package/dist/lexer/index.d.ts.map +1 -0
  34. package/dist/lexer/index.js +3 -0
  35. package/dist/lexer/index.js.map +1 -0
  36. package/dist/lexer/lexer.d.ts +36 -0
  37. package/dist/lexer/lexer.d.ts.map +1 -0
  38. package/dist/lexer/lexer.js +709 -0
  39. package/dist/lexer/lexer.js.map +1 -0
  40. package/dist/lexer/tokens.d.ts +45 -0
  41. package/dist/lexer/tokens.d.ts.map +1 -0
  42. package/dist/lexer/tokens.js +170 -0
  43. package/dist/lexer/tokens.js.map +1 -0
  44. package/dist/linter/directives.d.ts +71 -0
  45. package/dist/linter/directives.d.ts.map +1 -0
  46. package/dist/linter/directives.js +329 -0
  47. package/dist/linter/directives.js.map +1 -0
  48. package/dist/linter/index.d.ts +6 -0
  49. package/dist/linter/index.d.ts.map +1 -0
  50. package/dist/linter/index.js +4 -0
  51. package/dist/linter/index.js.map +1 -0
  52. package/dist/linter/linter.d.ts +39 -0
  53. package/dist/linter/linter.d.ts.map +1 -0
  54. package/dist/linter/linter.js +292 -0
  55. package/dist/linter/linter.js.map +1 -0
  56. package/dist/linter/rules/correct-indentation.d.ts +22 -0
  57. package/dist/linter/rules/correct-indentation.d.ts.map +1 -0
  58. package/dist/linter/rules/correct-indentation.js +111 -0
  59. package/dist/linter/rules/correct-indentation.js.map +1 -0
  60. package/dist/linter/rules/goto-needs-reset-in-events.d.ts +28 -0
  61. package/dist/linter/rules/goto-needs-reset-in-events.d.ts.map +1 -0
  62. package/dist/linter/rules/goto-needs-reset-in-events.js +88 -0
  63. package/dist/linter/rules/goto-needs-reset-in-events.js.map +1 -0
  64. package/dist/linter/rules/indent-style.d.ts +3 -0
  65. package/dist/linter/rules/indent-style.d.ts.map +1 -0
  66. package/dist/linter/rules/indent-style.js +41 -0
  67. package/dist/linter/rules/indent-style.js.map +1 -0
  68. package/dist/linter/rules/index.d.ts +5 -0
  69. package/dist/linter/rules/index.d.ts.map +1 -0
  70. package/dist/linter/rules/index.js +47 -0
  71. package/dist/linter/rules/index.js.map +1 -0
  72. package/dist/linter/rules/no-duplicate-labels.d.ts +3 -0
  73. package/dist/linter/rules/no-duplicate-labels.d.ts.map +1 -0
  74. package/dist/linter/rules/no-duplicate-labels.js +75 -0
  75. package/dist/linter/rules/no-duplicate-labels.js.map +1 -0
  76. package/dist/linter/rules/no-empty-blocks.d.ts +15 -0
  77. package/dist/linter/rules/no-empty-blocks.d.ts.map +1 -0
  78. package/dist/linter/rules/no-empty-blocks.js +45 -0
  79. package/dist/linter/rules/no-empty-blocks.js.map +1 -0
  80. package/dist/linter/rules/no-inline-argument.d.ts +14 -0
  81. package/dist/linter/rules/no-inline-argument.d.ts.map +1 -0
  82. package/dist/linter/rules/no-inline-argument.js +56 -0
  83. package/dist/linter/rules/no-inline-argument.js.map +1 -0
  84. package/dist/linter/rules/no-invalid-goto.d.ts +3 -0
  85. package/dist/linter/rules/no-invalid-goto.d.ts.map +1 -0
  86. package/dist/linter/rules/no-invalid-goto.js +92 -0
  87. package/dist/linter/rules/no-invalid-goto.js.map +1 -0
  88. package/dist/linter/rules/no-single-quotes.d.ts +3 -0
  89. package/dist/linter/rules/no-single-quotes.d.ts.map +1 -0
  90. package/dist/linter/rules/no-single-quotes.js +73 -0
  91. package/dist/linter/rules/no-single-quotes.js.map +1 -0
  92. package/dist/linter/rules/no-unclosed-bracket.d.ts +3 -0
  93. package/dist/linter/rules/no-unclosed-bracket.d.ts.map +1 -0
  94. package/dist/linter/rules/no-unclosed-bracket.js +108 -0
  95. package/dist/linter/rules/no-unclosed-bracket.js.map +1 -0
  96. package/dist/linter/rules/no-unclosed-string.d.ts +3 -0
  97. package/dist/linter/rules/no-unclosed-string.d.ts.map +1 -0
  98. package/dist/linter/rules/no-unclosed-string.js +48 -0
  99. package/dist/linter/rules/no-unclosed-string.js.map +1 -0
  100. package/dist/linter/rules/no-undefined-vars.d.ts +3 -0
  101. package/dist/linter/rules/no-undefined-vars.d.ts.map +1 -0
  102. package/dist/linter/rules/no-undefined-vars.js +257 -0
  103. package/dist/linter/rules/no-undefined-vars.js.map +1 -0
  104. package/dist/linter/rules/no-unreachable-code.d.ts +3 -0
  105. package/dist/linter/rules/no-unreachable-code.d.ts.map +1 -0
  106. package/dist/linter/rules/no-unreachable-code.js +215 -0
  107. package/dist/linter/rules/no-unreachable-code.js.map +1 -0
  108. package/dist/linter/rules/no-unused-labels.d.ts +3 -0
  109. package/dist/linter/rules/no-unused-labels.d.ts.map +1 -0
  110. package/dist/linter/rules/no-unused-labels.js +77 -0
  111. package/dist/linter/rules/no-unused-labels.js.map +1 -0
  112. package/dist/linter/rules/no-unused-vars.d.ts +3 -0
  113. package/dist/linter/rules/no-unused-vars.d.ts.map +1 -0
  114. package/dist/linter/rules/no-unused-vars.js +288 -0
  115. package/dist/linter/rules/no-unused-vars.js.map +1 -0
  116. package/dist/linter/rules/purchase-subkeyword-constraints.d.ts +15 -0
  117. package/dist/linter/rules/purchase-subkeyword-constraints.d.ts.map +1 -0
  118. package/dist/linter/rules/purchase-subkeyword-constraints.js +88 -0
  119. package/dist/linter/rules/purchase-subkeyword-constraints.js.map +1 -0
  120. package/dist/linter/rules/required-subkeywords.d.ts +14 -0
  121. package/dist/linter/rules/required-subkeywords.d.ts.map +1 -0
  122. package/dist/linter/rules/required-subkeywords.js +64 -0
  123. package/dist/linter/rules/required-subkeywords.js.map +1 -0
  124. package/dist/linter/rules/valid-keyword.d.ts +3 -0
  125. package/dist/linter/rules/valid-keyword.d.ts.map +1 -0
  126. package/dist/linter/rules/valid-keyword.js +41 -0
  127. package/dist/linter/rules/valid-keyword.js.map +1 -0
  128. package/dist/linter/rules/valid-sub-keyword.d.ts +3 -0
  129. package/dist/linter/rules/valid-sub-keyword.d.ts.map +1 -0
  130. package/dist/linter/rules/valid-sub-keyword.js +89 -0
  131. package/dist/linter/rules/valid-sub-keyword.js.map +1 -0
  132. package/dist/linter/rules/valid-subkeyword-value.d.ts +16 -0
  133. package/dist/linter/rules/valid-subkeyword-value.d.ts.map +1 -0
  134. package/dist/linter/rules/valid-subkeyword-value.js +100 -0
  135. package/dist/linter/rules/valid-subkeyword-value.js.map +1 -0
  136. package/dist/parser/ast.d.ts +119 -0
  137. package/dist/parser/ast.d.ts.map +1 -0
  138. package/dist/parser/ast.js +58 -0
  139. package/dist/parser/ast.js.map +1 -0
  140. package/dist/parser/index.d.ts +3 -0
  141. package/dist/parser/index.d.ts.map +1 -0
  142. package/dist/parser/index.js +3 -0
  143. package/dist/parser/index.js.map +1 -0
  144. package/dist/parser/parser.d.ts +55 -0
  145. package/dist/parser/parser.d.ts.map +1 -0
  146. package/dist/parser/parser.js +608 -0
  147. package/dist/parser/parser.js.map +1 -0
  148. package/dist/types.d.ts +44 -0
  149. package/dist/types.d.ts.map +1 -0
  150. package/dist/types.js +36 -0
  151. package/dist/types.js.map +1 -0
  152. package/package.json +49 -0
@@ -0,0 +1,47 @@
1
+ import { noUndefinedVars } from './no-undefined-vars.js';
2
+ import { noUnusedVars } from './no-unused-vars.js';
3
+ import { validKeyword } from './valid-keyword.js';
4
+ import { validSubKeyword } from './valid-sub-keyword.js';
5
+ import { noInvalidGoto } from './no-invalid-goto.js';
6
+ import { indentStyle } from './indent-style.js';
7
+ import { noUnclosedString } from './no-unclosed-string.js';
8
+ import { noUnclosedBracket } from './no-unclosed-bracket.js';
9
+ import { noSingleQuotes } from './no-single-quotes.js';
10
+ import { noUnreachableCode } from './no-unreachable-code.js';
11
+ import { requiredSubkeywords } from './required-subkeywords.js';
12
+ import { validSubkeywordValue } from './valid-subkeyword-value.js';
13
+ import { noInlineArgument } from './no-inline-argument.js';
14
+ import { gotoNeedsResetInEvents } from './goto-needs-reset-in-events.js';
15
+ import { purchaseSubkeywordConstraints } from './purchase-subkeyword-constraints.js';
16
+ import { correctIndentation } from './correct-indentation.js';
17
+ import { noDuplicateLabels } from './no-duplicate-labels.js';
18
+ import { noUnusedLabels } from './no-unused-labels.js';
19
+ import { noEmptyBlocks } from './no-empty-blocks.js';
20
+ export const rules = {
21
+ 'no-undefined-vars': noUndefinedVars,
22
+ 'no-unused-vars': noUnusedVars,
23
+ 'valid-keyword': validKeyword,
24
+ 'valid-sub-keyword': validSubKeyword,
25
+ 'no-invalid-goto': noInvalidGoto,
26
+ 'indent-style': indentStyle,
27
+ 'no-unclosed-string': noUnclosedString,
28
+ 'no-unclosed-bracket': noUnclosedBracket,
29
+ 'no-single-quotes': noSingleQuotes,
30
+ 'no-unreachable-code': noUnreachableCode,
31
+ 'required-subkeywords': requiredSubkeywords,
32
+ 'valid-subkeyword-value': validSubkeywordValue,
33
+ 'no-inline-argument': noInlineArgument,
34
+ 'goto-needs-reset-in-events': gotoNeedsResetInEvents,
35
+ 'purchase-subkeyword-constraints': purchaseSubkeywordConstraints,
36
+ 'correct-indentation': correctIndentation,
37
+ 'no-duplicate-labels': noDuplicateLabels,
38
+ 'no-unused-labels': noUnusedLabels,
39
+ 'no-empty-blocks': noEmptyBlocks,
40
+ };
41
+ export function getRule(name) {
42
+ return rules[name];
43
+ }
44
+ export function getAllRules() {
45
+ return Object.values(rules);
46
+ }
47
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/linter/rules/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,6BAA6B,EAAE,MAAM,sCAAsC,CAAC;AACrF,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,CAAC,MAAM,KAAK,GAA6B;IAC7C,mBAAmB,EAAE,eAAe;IACpC,gBAAgB,EAAE,YAAY;IAC9B,eAAe,EAAE,YAAY;IAC7B,mBAAmB,EAAE,eAAe;IACpC,iBAAiB,EAAE,aAAa;IAChC,cAAc,EAAE,WAAW;IAC3B,oBAAoB,EAAE,gBAAgB;IACtC,qBAAqB,EAAE,iBAAiB;IACxC,kBAAkB,EAAE,cAAc;IAClC,qBAAqB,EAAE,iBAAiB;IACxC,sBAAsB,EAAE,mBAAmB;IAC3C,wBAAwB,EAAE,oBAAoB;IAC9C,oBAAoB,EAAE,gBAAgB;IACtC,4BAA4B,EAAE,sBAAsB;IACpD,iCAAiC,EAAE,6BAA6B;IAChE,qBAAqB,EAAE,kBAAkB;IACzC,qBAAqB,EAAE,iBAAiB;IACxC,kBAAkB,EAAE,cAAc;IAClC,iBAAiB,EAAE,aAAa;CACjC,CAAC;AAEF,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC9B,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { LintRule } from '../linter.js';
2
+ export declare const noDuplicateLabels: LintRule;
3
+ //# sourceMappingURL=no-duplicate-labels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-duplicate-labels.d.ts","sourceRoot":"","sources":["../../../src/linter/rules/no-duplicate-labels.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,cAAc,CAAC;AAG1D,eAAO,MAAM,iBAAiB,EAAE,QA6E/B,CAAC"}
@@ -0,0 +1,75 @@
1
+ export const noDuplicateLabels = {
2
+ name: 'no-duplicate-labels',
3
+ description: 'Disallow duplicate *label definitions',
4
+ severity: 'error',
5
+ create(context) {
6
+ const labelDefinitions = [];
7
+ function collectLabels(node) {
8
+ if (!node || typeof node !== 'object')
9
+ return;
10
+ if (node.type === 'Program') {
11
+ for (const stmt of node.body) {
12
+ collectLabels(stmt);
13
+ }
14
+ }
15
+ else if (node.type === 'KeywordStatement') {
16
+ const kw = node;
17
+ if (kw.keyword === 'label' && kw.argument) {
18
+ let labelName = '';
19
+ if (kw.argument.type === 'TextContent') {
20
+ const text = kw.argument.parts.find(p => typeof p === 'string');
21
+ if (text) {
22
+ labelName = text.trim();
23
+ }
24
+ }
25
+ else if (kw.argument.type === 'Identifier') {
26
+ labelName = kw.argument.name;
27
+ }
28
+ if (labelName) {
29
+ labelDefinitions.push({
30
+ name: labelName,
31
+ line: kw.loc.start.line,
32
+ column: kw.loc.start.column,
33
+ });
34
+ }
35
+ }
36
+ // Recurse into body
37
+ for (const stmt of kw.body) {
38
+ collectLabels(stmt);
39
+ }
40
+ // Recurse into sub-keywords
41
+ for (const sub of kw.subKeywords) {
42
+ for (const stmt of sub.body) {
43
+ collectLabels(stmt);
44
+ }
45
+ }
46
+ }
47
+ else if (node.type === 'AnswerOption') {
48
+ for (const stmt of node.body) {
49
+ collectLabels(stmt);
50
+ }
51
+ }
52
+ }
53
+ return {
54
+ Program(node) {
55
+ collectLabels(node);
56
+ // Group labels by name and report duplicates
57
+ const seen = new Map();
58
+ for (const label of labelDefinitions) {
59
+ const first = seen.get(label.name);
60
+ if (first) {
61
+ context.report({
62
+ message: `Duplicate label '${label.name}' (first defined on line ${first.line})`,
63
+ line: label.line,
64
+ column: label.column,
65
+ });
66
+ }
67
+ else {
68
+ seen.set(label.name, { line: label.line, column: label.column });
69
+ }
70
+ }
71
+ },
72
+ };
73
+ },
74
+ };
75
+ //# sourceMappingURL=no-duplicate-labels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-duplicate-labels.js","sourceRoot":"","sources":["../../../src/linter/rules/no-duplicate-labels.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { LintRule } from '../linter.js';
2
+ /**
3
+ * Rule: no-empty-blocks
4
+ *
5
+ * Detects empty control flow and body-required keyword blocks.
6
+ * An empty block is likely an authoring mistake where the body was left blank.
7
+ *
8
+ * Examples of violations:
9
+ * - `*if: x > 5` with no indented body
10
+ * - `*while: running` with no indented body
11
+ * - `*page:` with no indented body
12
+ * - `*else:` with no indented body
13
+ */
14
+ export declare const noEmptyBlocks: LintRule;
15
+ //# sourceMappingURL=no-empty-blocks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-empty-blocks.d.ts","sourceRoot":"","sources":["../../../src/linter/rules/no-empty-blocks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,cAAc,CAAC;AAI1D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa,EAAE,QAiC3B,CAAC"}
@@ -0,0 +1,45 @@
1
+ import { getKeywordSpec } from '../../language/keyword-spec.js';
2
+ /**
3
+ * Rule: no-empty-blocks
4
+ *
5
+ * Detects empty control flow and body-required keyword blocks.
6
+ * An empty block is likely an authoring mistake where the body was left blank.
7
+ *
8
+ * Examples of violations:
9
+ * - `*if: x > 5` with no indented body
10
+ * - `*while: running` with no indented body
11
+ * - `*page:` with no indented body
12
+ * - `*else:` with no indented body
13
+ */
14
+ export const noEmptyBlocks = {
15
+ name: 'no-empty-blocks',
16
+ description: 'Disallow empty keyword blocks',
17
+ severity: 'error',
18
+ create(context) {
19
+ return {
20
+ KeywordStatement(node) {
21
+ if (node.body.length > 0 || node.subKeywords.length > 0)
22
+ return;
23
+ const keyword = node.keyword.toLowerCase();
24
+ // else and elseif are not in KEYWORD_SPECS but should always have a body
25
+ if (keyword === 'else' || keyword === 'elseif') {
26
+ context.report({
27
+ message: `\`*${keyword}:\` block is empty`,
28
+ line: node.loc.start.line,
29
+ column: node.loc.start.column,
30
+ });
31
+ return;
32
+ }
33
+ const spec = getKeywordSpec(keyword);
34
+ if (spec?.body.required && node.body.length === 0) {
35
+ context.report({
36
+ message: `\`*${keyword}:\` block is empty`,
37
+ line: node.loc.start.line,
38
+ column: node.loc.start.column,
39
+ });
40
+ }
41
+ },
42
+ };
43
+ },
44
+ };
45
+ //# sourceMappingURL=no-empty-blocks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-empty-blocks.js","sourceRoot":"","sources":["../../../src/linter/rules/no-empty-blocks.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAEhE;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,aAAa,GAAa;IACrC,IAAI,EAAE,iBAAiB;IACvB,WAAW,EAAE,+BAA+B;IAC5C,QAAQ,EAAE,OAAO;IAEjB,MAAM,CAAC,OAAoB;QACzB,OAAO;YACL,gBAAgB,CAAC,IAAsB;gBACrC,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC;oBAAE,OAAO;gBAEhE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;gBAE3C,yEAAyE;gBACzE,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;oBAC/C,OAAO,CAAC,MAAM,CAAC;wBACb,OAAO,EAAE,MAAM,OAAO,oBAAoB;wBAC1C,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI;wBACzB,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM;qBAC9B,CAAC,CAAC;oBACH,OAAO;gBACT,CAAC;gBAED,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;gBACrC,IAAI,IAAI,EAAE,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBAClD,OAAO,CAAC,MAAM,CAAC;wBACb,OAAO,EAAE,MAAM,OAAO,oBAAoB;wBAC1C,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI;wBACzB,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM;qBAC9B,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { LintRule } from '../linter.js';
2
+ /**
3
+ * Rule: no-inline-argument
4
+ *
5
+ * Ensures that keywords that shouldn't have inline arguments don't have them.
6
+ *
7
+ * Examples of violations:
8
+ * - `*page: something` (should be just `*page`)
9
+ * - `*html: something` (should be just `*html`)
10
+ * - `*events: something` (should be just `*events`)
11
+ * - `*clear: something` (should be just `*clear`)
12
+ */
13
+ export declare const noInlineArgument: LintRule;
14
+ //# sourceMappingURL=no-inline-argument.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-inline-argument.d.ts","sourceRoot":"","sources":["../../../src/linter/rules/no-inline-argument.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,cAAc,CAAC;AAI1D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,EAAE,QA6C9B,CAAC"}
@@ -0,0 +1,56 @@
1
+ import { getKeywordSpec } from '../../language/keyword-spec.js';
2
+ /**
3
+ * Rule: no-inline-argument
4
+ *
5
+ * Ensures that keywords that shouldn't have inline arguments don't have them.
6
+ *
7
+ * Examples of violations:
8
+ * - `*page: something` (should be just `*page`)
9
+ * - `*html: something` (should be just `*html`)
10
+ * - `*events: something` (should be just `*events`)
11
+ * - `*clear: something` (should be just `*clear`)
12
+ */
13
+ export const noInlineArgument = {
14
+ name: 'no-inline-argument',
15
+ description: 'Ensure keywords that should not have inline arguments do not have them',
16
+ severity: 'error',
17
+ create(context) {
18
+ function checkKeyword(node) {
19
+ const keyword = node.keyword.toLowerCase();
20
+ const spec = getKeywordSpec(keyword);
21
+ if (!spec)
22
+ return;
23
+ // Check if this keyword should not have an argument
24
+ if (spec.argument.type === 'none' && node.argument !== null) {
25
+ context.report({
26
+ message: `'*${keyword}' should not have an inline argument`,
27
+ line: node.loc.start.line,
28
+ column: node.loc.start.column,
29
+ });
30
+ }
31
+ }
32
+ function visit(node) {
33
+ if (node.type === 'Program') {
34
+ for (const stmt of node.body) {
35
+ if (stmt.type === 'KeywordStatement') {
36
+ visit(stmt);
37
+ }
38
+ }
39
+ }
40
+ else if (node.type === 'KeywordStatement') {
41
+ checkKeyword(node);
42
+ for (const stmt of node.body) {
43
+ if (stmt.type === 'KeywordStatement') {
44
+ visit(stmt);
45
+ }
46
+ }
47
+ }
48
+ }
49
+ return {
50
+ Program(node) {
51
+ visit(node);
52
+ },
53
+ };
54
+ },
55
+ };
56
+ //# sourceMappingURL=no-inline-argument.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-inline-argument.js","sourceRoot":"","sources":["../../../src/linter/rules/no-inline-argument.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAEhE;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAa;IACxC,IAAI,EAAE,oBAAoB;IAC1B,WAAW,EAAE,wEAAwE;IACrF,QAAQ,EAAE,OAAO;IAEjB,MAAM,CAAC,OAAoB;QACzB,SAAS,YAAY,CAAC,IAAsB;YAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;YAC3C,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;YAErC,IAAI,CAAC,IAAI;gBAAE,OAAO;YAElB,oDAAoD;YACpD,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;gBAC5D,OAAO,CAAC,MAAM,CAAC;oBACb,OAAO,EAAE,KAAK,OAAO,sCAAsC;oBAC3D,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI;oBACzB,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM;iBAC9B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,SAAS,KAAK,CAAC,IAAgC;YAC7C,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gBAC5B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;oBAC7B,IAAI,IAAI,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;wBACrC,KAAK,CAAC,IAAI,CAAC,CAAC;oBACd,CAAC;gBACH,CAAC;YACH,CAAC;iBAAM,IAAI,IAAI,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;gBAC5C,YAAY,CAAC,IAAI,CAAC,CAAC;gBACnB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;oBAC7B,IAAI,IAAI,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;wBACrC,KAAK,CAAC,IAAI,CAAC,CAAC;oBACd,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO;YACL,OAAO,CAAC,IAAa;gBACnB,KAAK,CAAC,IAAI,CAAC,CAAC;YACd,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { LintRule } from '../linter.js';
2
+ export declare const noInvalidGoto: LintRule;
3
+ //# sourceMappingURL=no-invalid-goto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-invalid-goto.d.ts","sourceRoot":"","sources":["../../../src/linter/rules/no-invalid-goto.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,cAAc,CAAC;AAG1D,eAAO,MAAM,aAAa,EAAE,QA8F3B,CAAC"}
@@ -0,0 +1,92 @@
1
+ export const noInvalidGoto = {
2
+ name: 'no-invalid-goto',
3
+ description: 'Ensure *goto targets exist',
4
+ severity: 'error',
5
+ create(context) {
6
+ const definedLabels = new Set();
7
+ const gotoStatements = [];
8
+ function collectLabels(node) {
9
+ if (!node || typeof node !== 'object')
10
+ return;
11
+ if (node.type === 'Program') {
12
+ for (const stmt of node.body) {
13
+ collectLabels(stmt);
14
+ }
15
+ }
16
+ else if (node.type === 'KeywordStatement') {
17
+ const kw = node;
18
+ // Collect *label: definitions
19
+ if (kw.keyword === 'label' && kw.argument) {
20
+ let labelName = '';
21
+ if (kw.argument.type === 'TextContent') {
22
+ const text = kw.argument.parts.find(p => typeof p === 'string');
23
+ if (text) {
24
+ labelName = text.trim();
25
+ }
26
+ }
27
+ else if (kw.argument.type === 'Identifier') {
28
+ labelName = kw.argument.name;
29
+ }
30
+ if (labelName) {
31
+ definedLabels.add(labelName);
32
+ }
33
+ }
34
+ // Collect *goto: usages
35
+ if (kw.keyword === 'goto' && kw.argument) {
36
+ let targetName = '';
37
+ if (kw.argument.type === 'TextContent') {
38
+ const text = kw.argument.parts.find(p => typeof p === 'string');
39
+ if (text) {
40
+ targetName = text.trim();
41
+ }
42
+ }
43
+ else if (kw.argument.type === 'Identifier') {
44
+ targetName = kw.argument.name;
45
+ }
46
+ if (targetName) {
47
+ // Skip URLs (external gotos)
48
+ if (!targetName.startsWith('http://') && !targetName.startsWith('https://')) {
49
+ gotoStatements.push({
50
+ target: targetName,
51
+ line: kw.loc.start.line,
52
+ column: kw.loc.start.column,
53
+ });
54
+ }
55
+ }
56
+ }
57
+ // Recurse into body
58
+ for (const stmt of kw.body) {
59
+ collectLabels(stmt);
60
+ }
61
+ // Recurse into sub-keywords
62
+ for (const sub of kw.subKeywords) {
63
+ for (const stmt of sub.body) {
64
+ collectLabels(stmt);
65
+ }
66
+ }
67
+ }
68
+ else if (node.type === 'AnswerOption') {
69
+ for (const stmt of node.body) {
70
+ collectLabels(stmt);
71
+ }
72
+ }
73
+ }
74
+ return {
75
+ Program(node) {
76
+ // Collect all label definitions and goto usages
77
+ collectLabels(node);
78
+ // Report gotos that target undefined labels
79
+ for (const goto of gotoStatements) {
80
+ if (!definedLabels.has(goto.target)) {
81
+ context.report({
82
+ message: `*goto target '${goto.target}' is not defined`,
83
+ line: goto.line,
84
+ column: goto.column,
85
+ });
86
+ }
87
+ }
88
+ },
89
+ };
90
+ },
91
+ };
92
+ //# sourceMappingURL=no-invalid-goto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-invalid-goto.js","sourceRoot":"","sources":["../../../src/linter/rules/no-invalid-goto.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { LintRule } from '../linter.js';
2
+ export declare const noSingleQuotes: LintRule;
3
+ //# sourceMappingURL=no-single-quotes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-single-quotes.d.ts","sourceRoot":"","sources":["../../../src/linter/rules/no-single-quotes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,cAAc,CAAC;AAG1D,eAAO,MAAM,cAAc,EAAE,QAkD5B,CAAC"}
@@ -0,0 +1,73 @@
1
+ export const noSingleQuotes = {
2
+ name: 'no-single-quotes',
3
+ description: 'Disallow single quotes for strings (use double quotes)',
4
+ severity: 'error',
5
+ create(context) {
6
+ return {
7
+ Program(_node) {
8
+ const source = context.getSourceCode();
9
+ const lines = source.split(/\r?\n/);
10
+ lines.forEach((line, lineIndex) => {
11
+ const lineNumber = lineIndex + 1;
12
+ const trimmedLine = line.trimStart();
13
+ // Skip blank lines
14
+ if (trimmedLine.length === 0)
15
+ return;
16
+ // Skip comments (allowed to have single quotes)
17
+ if (trimmedLine.startsWith('--'))
18
+ return;
19
+ // Check for expression lines (>> ...)
20
+ if (trimmedLine.startsWith('>>')) {
21
+ checkExpressionForSingleQuotes(line, lineNumber, context);
22
+ return;
23
+ }
24
+ // Check for conditional keywords (*if:, *while:, etc.) with expressions
25
+ const conditionalMatch = trimmedLine.match(/^\*(?:if|while|for):\s*(.+)$/);
26
+ if (conditionalMatch) {
27
+ const expression = conditionalMatch[1];
28
+ const exprStartCol = line.indexOf(expression) + 1;
29
+ checkStringForSingleQuotes(expression, lineNumber, exprStartCol, context);
30
+ return;
31
+ }
32
+ // Check for *path: lines (they may contain expressions with strings)
33
+ const pathMatch = trimmedLine.match(/^\*path:\s*(.+)$/);
34
+ if (pathMatch) {
35
+ const pathValue = pathMatch[1];
36
+ const pathStartCol = line.indexOf(pathValue) + 1;
37
+ checkStringForSingleQuotes(pathValue, lineNumber, pathStartCol, context);
38
+ return;
39
+ }
40
+ // All other lines (visible text, keyword arguments, answer options) are allowed to have single quotes
41
+ });
42
+ },
43
+ };
44
+ },
45
+ };
46
+ function checkExpressionForSingleQuotes(line, lineNumber, context) {
47
+ const exprMatch = line.match(/^(\t*)>>\s*(.+)$/);
48
+ if (!exprMatch)
49
+ return;
50
+ const expression = exprMatch[2];
51
+ const exprStartCol = line.indexOf(expression) + 1;
52
+ checkStringForSingleQuotes(expression, lineNumber, exprStartCol, context);
53
+ }
54
+ function checkStringForSingleQuotes(text, lineNumber, startCol, context) {
55
+ let inDoubleQuotes = false;
56
+ for (let i = 0; i < text.length; i++) {
57
+ const ch = text[i];
58
+ // Toggle double quote state
59
+ if (ch === '"') {
60
+ inDoubleQuotes = !inDoubleQuotes;
61
+ continue;
62
+ }
63
+ // If we find a single quote outside of double quotes, that's an error
64
+ if (ch === "'" && !inDoubleQuotes) {
65
+ context.report({
66
+ message: "Single quotes are not valid for strings in GuidedTrack. Use double quotes instead.",
67
+ line: lineNumber,
68
+ column: startCol + i,
69
+ });
70
+ }
71
+ }
72
+ }
73
+ //# sourceMappingURL=no-single-quotes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-single-quotes.js","sourceRoot":"","sources":["../../../src/linter/rules/no-single-quotes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { LintRule } from '../linter.js';
2
+ export declare const noUnclosedBracket: LintRule;
3
+ //# sourceMappingURL=no-unclosed-bracket.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-unclosed-bracket.d.ts","sourceRoot":"","sources":["../../../src/linter/rules/no-unclosed-bracket.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,cAAc,CAAC;AAS1D,eAAO,MAAM,iBAAiB,EAAE,QAoH/B,CAAC"}
@@ -0,0 +1,108 @@
1
+ export const noUnclosedBracket = {
2
+ name: 'no-unclosed-bracket',
3
+ description: 'Detect unclosed brackets/braces',
4
+ severity: 'error',
5
+ create(context) {
6
+ return {
7
+ Program(_node) {
8
+ const source = context.getSourceCode();
9
+ const lines = source.split('\n');
10
+ const stack = [];
11
+ const pairs = {
12
+ '(': ')',
13
+ '[': ']',
14
+ '{': '}',
15
+ };
16
+ const closers = {
17
+ ')': '(',
18
+ ']': '[',
19
+ '}': '{',
20
+ };
21
+ // Expression keywords that are followed by expressions
22
+ const exprKeywords = ['if', 'while', 'for', 'repeat', 'goto', 'return'];
23
+ for (let i = 0; i < lines.length; i++) {
24
+ const line = lines[i];
25
+ const lineNumber = i + 1;
26
+ const trimmedLine = line.trim();
27
+ // Only check brackets in expression contexts:
28
+ // 1. Lines starting with >>
29
+ // 2. Lines starting with expression keywords (*if:, *while:, etc.)
30
+ let isExpressionContext = false;
31
+ if (trimmedLine.startsWith('>>')) {
32
+ isExpressionContext = true;
33
+ }
34
+ else if (trimmedLine.startsWith('*')) {
35
+ // Check if it's an expression keyword
36
+ for (const keyword of exprKeywords) {
37
+ if (trimmedLine.toLowerCase().startsWith(`*${keyword}:`)) {
38
+ isExpressionContext = true;
39
+ break;
40
+ }
41
+ }
42
+ }
43
+ // Skip checking if not in an expression context
44
+ if (!isExpressionContext)
45
+ continue;
46
+ let inString = false;
47
+ let stringChar = '';
48
+ for (let j = 0; j < line.length; j++) {
49
+ const ch = line[j];
50
+ // Skip if in comment
51
+ if (!inString && ch === '-' && line[j + 1] === '-') {
52
+ break;
53
+ }
54
+ // Handle strings
55
+ if (!inString && (ch === '"' || ch === "'")) {
56
+ inString = true;
57
+ stringChar = ch;
58
+ continue;
59
+ }
60
+ if (inString && ch === stringChar) {
61
+ inString = false;
62
+ stringChar = '';
63
+ continue;
64
+ }
65
+ // Skip bracket checking inside strings
66
+ if (inString)
67
+ continue;
68
+ // Opening brackets
69
+ if (pairs[ch]) {
70
+ stack.push({ char: ch, line: lineNumber, column: j + 1 });
71
+ }
72
+ // Closing brackets
73
+ if (closers[ch]) {
74
+ if (stack.length === 0) {
75
+ context.report({
76
+ message: `Unexpected closing bracket '${ch}'`,
77
+ line: lineNumber,
78
+ column: j + 1,
79
+ });
80
+ }
81
+ else {
82
+ const top = stack.pop();
83
+ if (pairs[top.char] !== ch) {
84
+ context.report({
85
+ message: `Mismatched brackets: expected '${pairs[top.char]}' but found '${ch}'`,
86
+ line: lineNumber,
87
+ column: j + 1,
88
+ });
89
+ // Put back the opener since it's not matched
90
+ stack.push(top);
91
+ }
92
+ }
93
+ }
94
+ }
95
+ }
96
+ // Report any unclosed brackets
97
+ for (const bracket of stack) {
98
+ context.report({
99
+ message: `Unclosed bracket '${bracket.char}' (missing '${pairs[bracket.char]}')`,
100
+ line: bracket.line,
101
+ column: bracket.column,
102
+ });
103
+ }
104
+ },
105
+ };
106
+ },
107
+ };
108
+ //# sourceMappingURL=no-unclosed-bracket.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-unclosed-bracket.js","sourceRoot":"","sources":["../../../src/linter/rules/no-unclosed-bracket.ts"],"names":[],"mappings":"AASA,MAAM,CAAC,MAAM,iBAAiB,GAAa;IACzC,IAAI,EAAE,qBAAqB;IAC3B,WAAW,EAAE,iCAAiC;IAC9C,QAAQ,EAAE,OAAO;IAEjB,MAAM,CAAC,OAAoB;QACzB,OAAO;YACL,OAAO,CAAC,KAAc;gBACpB,MAAM,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;gBACvC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAEjC,MAAM,KAAK,GAAkB,EAAE,CAAC;gBAChC,MAAM,KAAK,GAA2B;oBACpC,GAAG,EAAE,GAAG;oBACR,GAAG,EAAE,GAAG;oBACR,GAAG,EAAE,GAAG;iBACT,CAAC;gBACF,MAAM,OAAO,GAA2B;oBACtC,GAAG,EAAE,GAAG;oBACR,GAAG,EAAE,GAAG;oBACR,GAAG,EAAE,GAAG;iBACT,CAAC;gBAEF,uDAAuD;gBACvD,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;gBAExE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;oBACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;oBACtB,MAAM,UAAU,GAAG,CAAC,GAAG,CAAC,CAAC;oBACzB,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;oBAEhC,8CAA8C;oBAC9C,4BAA4B;oBAC5B,mEAAmE;oBACnE,IAAI,mBAAmB,GAAG,KAAK,CAAC;oBAChC,IAAI,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;wBACjC,mBAAmB,GAAG,IAAI,CAAC;oBAC7B,CAAC;yBAAM,IAAI,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;wBACvC,sCAAsC;wBACtC,KAAK,MAAM,OAAO,IAAI,YAAY,EAAE,CAAC;4BACnC,IAAI,WAAW,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;gCACzD,mBAAmB,GAAG,IAAI,CAAC;gCAC3B,MAAM;4BACR,CAAC;wBACH,CAAC;oBACH,CAAC;oBAED,gDAAgD;oBAChD,IAAI,CAAC,mBAAmB;wBAAE,SAAS;oBAEnC,IAAI,QAAQ,GAAG,KAAK,CAAC;oBACrB,IAAI,UAAU,GAAG,EAAE,CAAC;oBAEpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;wBACrC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;wBAEnB,qBAAqB;wBACrB,IAAI,CAAC,QAAQ,IAAI,EAAE,KAAK,GAAG,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;4BACnD,MAAM;wBACR,CAAC;wBAED,iBAAiB;wBACjB,IAAI,CAAC,QAAQ,IAAI,CAAC,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC;4BAC5C,QAAQ,GAAG,IAAI,CAAC;4BAChB,UAAU,GAAG,EAAE,CAAC;4BAChB,SAAS;wBACX,CAAC;wBACD,IAAI,QAAQ,IAAI,EAAE,KAAK,UAAU,EAAE,CAAC;4BAClC,QAAQ,GAAG,KAAK,CAAC;4BACjB,UAAU,GAAG,EAAE,CAAC;4BAChB,SAAS;wBACX,CAAC;wBAED,uCAAuC;wBACvC,IAAI,QAAQ;4BAAE,SAAS;wBAEvB,mBAAmB;wBACnB,IAAI,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;4BACd,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;wBAC5D,CAAC;wBAED,mBAAmB;wBACnB,IAAI,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;4BAChB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gCACvB,OAAO,CAAC,MAAM,CAAC;oCACb,OAAO,EAAE,+BAA+B,EAAE,GAAG;oCAC7C,IAAI,EAAE,UAAU;oCAChB,MAAM,EAAE,CAAC,GAAG,CAAC;iCACd,CAAC,CAAC;4BACL,CAAC;iCAAM,CAAC;gCACN,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,EAAG,CAAC;gCACzB,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;oCAC3B,OAAO,CAAC,MAAM,CAAC;wCACb,OAAO,EAAE,kCAAkC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,gBAAgB,EAAE,GAAG;wCAC/E,IAAI,EAAE,UAAU;wCAChB,MAAM,EAAE,CAAC,GAAG,CAAC;qCACd,CAAC,CAAC;oCACH,6CAA6C;oCAC7C,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gCAClB,CAAC;4BACH,CAAC;wBACH,CAAC;oBACH,CAAC;gBACH,CAAC;gBAED,+BAA+B;gBAC/B,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE,CAAC;oBAC5B,OAAO,CAAC,MAAM,CAAC;wBACb,OAAO,EAAE,qBAAqB,OAAO,CAAC,IAAI,eAAe,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI;wBAChF,IAAI,EAAE,OAAO,CAAC,IAAI;wBAClB,MAAM,EAAE,OAAO,CAAC,MAAM;qBACvB,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { LintRule } from '../linter.js';
2
+ export declare const noUnclosedString: LintRule;
3
+ //# sourceMappingURL=no-unclosed-string.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-unclosed-string.d.ts","sourceRoot":"","sources":["../../../src/linter/rules/no-unclosed-string.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,cAAc,CAAC;AAG1D,eAAO,MAAM,gBAAgB,EAAE,QAoD9B,CAAC"}