skir 0.0.3 → 0.0.6
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/dist/casing.d.ts.map +1 -1
- package/dist/casing.js +4 -1
- package/dist/casing.js.map +1 -1
- package/dist/casing.test.js +35 -1
- package/dist/casing.test.js.map +1 -1
- package/dist/compatibility_checker.test.js +2 -2
- package/dist/compiler.js +23 -42
- package/dist/compiler.js.map +1 -1
- package/dist/config.d.ts +2 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +5 -10
- package/dist/config.js.map +1 -1
- package/dist/config_parser.d.ts +25 -0
- package/dist/config_parser.d.ts.map +1 -0
- package/dist/config_parser.js +125 -0
- package/dist/config_parser.js.map +1 -0
- package/dist/config_parser.test.d.ts +2 -0
- package/dist/config_parser.test.d.ts.map +1 -0
- package/dist/config_parser.test.js +386 -0
- package/dist/config_parser.test.js.map +1 -0
- package/dist/doc_comment_parser.d.ts +3 -2
- package/dist/doc_comment_parser.d.ts.map +1 -1
- package/dist/doc_comment_parser.js +67 -52
- package/dist/doc_comment_parser.js.map +1 -1
- package/dist/doc_comment_parser.test.js +86 -154
- package/dist/doc_comment_parser.test.js.map +1 -1
- package/dist/error_renderer.d.ts +4 -0
- package/dist/error_renderer.d.ts.map +1 -1
- package/dist/error_renderer.js +21 -0
- package/dist/error_renderer.js.map +1 -1
- package/dist/module_set.d.ts.map +1 -1
- package/dist/module_set.js +29 -12
- package/dist/module_set.js.map +1 -1
- package/dist/module_set.test.js +318 -173
- package/dist/module_set.test.js.map +1 -1
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +10 -10
- package/dist/parser.js.map +1 -1
- package/dist/project_initializer.js +9 -1
- package/dist/project_initializer.js.map +1 -1
- package/dist/tokenizer.d.ts +7 -1
- package/dist/tokenizer.d.ts.map +1 -1
- package/dist/tokenizer.js +12 -0
- package/dist/tokenizer.js.map +1 -1
- package/package.json +10 -5
- package/src/casing.ts +6 -1
- package/src/compiler.ts +26 -40
- package/src/config.ts +10 -15
- package/src/config_parser.ts +169 -0
- package/src/doc_comment_parser.ts +76 -52
- package/src/error_renderer.ts +34 -0
- package/src/module_set.ts +31 -15
- package/src/parser.ts +16 -10
- package/src/project_initializer.ts +9 -1
- package/src/tokenizer.ts +20 -2
- package/dist/language_server.d.ts +0 -15
- package/dist/language_server.d.ts.map +0 -1
- package/dist/language_server.js +0 -248
- package/dist/language_server.js.map +0 -1
- package/src/language_server.ts +0 -301
package/src/compiler.ts
CHANGED
|
@@ -4,15 +4,15 @@ import { glob } from "glob";
|
|
|
4
4
|
import * as paths from "path";
|
|
5
5
|
import type { CodeGenerator } from "skir-internal";
|
|
6
6
|
import Watcher from "watcher";
|
|
7
|
-
import * as yaml from "yaml";
|
|
8
|
-
import { fromZodError } from "zod-validation-error";
|
|
9
7
|
import { parseCommandLine } from "./command_line_parser.js";
|
|
10
|
-
import { GeneratorConfig
|
|
8
|
+
import { GeneratorConfig } from "./config.js";
|
|
9
|
+
import { importCodeGenerator, parseSkirConfig } from "./config_parser.js";
|
|
11
10
|
import {
|
|
12
11
|
makeGray,
|
|
13
12
|
makeGreen,
|
|
14
13
|
makeRed,
|
|
15
14
|
renderErrors,
|
|
15
|
+
renderSkirConfigErrors,
|
|
16
16
|
} from "./error_renderer.js";
|
|
17
17
|
import { formatModule } from "./formatter.js";
|
|
18
18
|
import { REAL_FILE_SYSTEM } from "./io.js";
|
|
@@ -33,32 +33,19 @@ async function makeGeneratorBundle(
|
|
|
33
33
|
config: GeneratorConfig,
|
|
34
34
|
root: string,
|
|
35
35
|
): Promise<GeneratorBundle> {
|
|
36
|
-
const
|
|
37
|
-
const generator = mod.GENERATOR;
|
|
38
|
-
if (typeof generator !== "object") {
|
|
39
|
-
throw new Error(`Cannot import GENERATOR from module ${config.mod}`);
|
|
40
|
-
}
|
|
41
|
-
// Validate the generator config.
|
|
42
|
-
const parsedConfig = generator.configType.safeParse(config.config);
|
|
43
|
-
if (!parsedConfig.success) {
|
|
44
|
-
const { id } = generator;
|
|
45
|
-
console.error(makeRed(`Invalid config for ${id} generator`));
|
|
46
|
-
const validationError = fromZodError(parsedConfig.error);
|
|
47
|
-
console.error(validationError.toString());
|
|
48
|
-
process.exit(1);
|
|
49
|
-
}
|
|
36
|
+
const generator = await importCodeGenerator(config.mod);
|
|
50
37
|
let skiroutDirs: string[];
|
|
51
|
-
if (config.
|
|
38
|
+
if (config.outDir === undefined) {
|
|
52
39
|
skiroutDirs = ["skirout"];
|
|
53
|
-
} else if (typeof config.
|
|
54
|
-
skiroutDirs = [config.
|
|
40
|
+
} else if (typeof config.outDir === "string") {
|
|
41
|
+
skiroutDirs = [config.outDir];
|
|
55
42
|
} else {
|
|
56
|
-
skiroutDirs = config.
|
|
43
|
+
skiroutDirs = config.outDir;
|
|
57
44
|
}
|
|
58
45
|
skiroutDirs = skiroutDirs.map((d) => paths.join(root, d));
|
|
59
46
|
return {
|
|
60
|
-
generator,
|
|
61
|
-
config:
|
|
47
|
+
generator: generator,
|
|
48
|
+
config: config.config,
|
|
62
49
|
skiroutDirs: skiroutDirs,
|
|
63
50
|
};
|
|
64
51
|
}
|
|
@@ -362,28 +349,27 @@ async function main(): Promise<void> {
|
|
|
362
349
|
}
|
|
363
350
|
}
|
|
364
351
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
352
|
+
let skirConfigPath = paths.join(root!, "skir.yml");
|
|
353
|
+
if (
|
|
354
|
+
!paths.isAbsolute(skirConfigPath) &&
|
|
355
|
+
!/^\.{1,2}[/\\]$/.test(skirConfigPath)
|
|
356
|
+
) {
|
|
357
|
+
// To make it clear that it's a path, prepend "./"
|
|
358
|
+
skirConfigPath = `.${paths.sep}${skirConfigPath}`;
|
|
359
|
+
}
|
|
360
|
+
const skirConfigCode = REAL_FILE_SYSTEM.readTextFile(skirConfigPath);
|
|
361
|
+
if (skirConfigCode === undefined) {
|
|
369
362
|
console.error(makeRed(`Cannot find ${skirConfigPath}`));
|
|
370
363
|
process.exit(1);
|
|
371
364
|
}
|
|
372
365
|
|
|
373
|
-
|
|
374
|
-
{
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
skirConfig = parseResult.data;
|
|
379
|
-
} else {
|
|
380
|
-
console.error(makeRed("Invalid skir config"));
|
|
381
|
-
console.error(` Path: ${skirConfigPath}`);
|
|
382
|
-
const validationError = fromZodError(parseResult.error);
|
|
383
|
-
console.error(validationError.toString());
|
|
384
|
-
process.exit(1);
|
|
385
|
-
}
|
|
366
|
+
const skirConfigResult = await parseSkirConfig(skirConfigCode, "import-mods");
|
|
367
|
+
if (skirConfigResult.errors.length > 0) {
|
|
368
|
+
console.error(makeRed("Invalid skir config"));
|
|
369
|
+
renderSkirConfigErrors(skirConfigResult.errors, { skirConfigPath });
|
|
370
|
+
process.exit(1);
|
|
386
371
|
}
|
|
372
|
+
const skirConfig = skirConfigResult.skirConfig!;
|
|
387
373
|
|
|
388
374
|
const srcDir = paths.join(root!, skirConfig.srcDir || ".");
|
|
389
375
|
|
package/src/config.ts
CHANGED
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
-
export const GeneratorConfig = z
|
|
4
|
-
.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
z.array(z.string().regex(/^.*\/skirout$/)),
|
|
14
|
-
])
|
|
15
|
-
.optional(),
|
|
16
|
-
})
|
|
17
|
-
.strict();
|
|
3
|
+
export const GeneratorConfig = z.strictObject({
|
|
4
|
+
mod: z.string(),
|
|
5
|
+
config: z.any(),
|
|
6
|
+
outDir: z
|
|
7
|
+
.union([
|
|
8
|
+
z.string().endsWith("/skirout"),
|
|
9
|
+
z.array(z.string().endsWith("/skirout")),
|
|
10
|
+
])
|
|
11
|
+
.optional(),
|
|
12
|
+
});
|
|
18
13
|
|
|
19
14
|
export type GeneratorConfig = z.infer<typeof GeneratorConfig>;
|
|
20
15
|
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import * as ccGen from "skir-cc-gen";
|
|
2
|
+
import * as dartGen from "skir-dart-gen";
|
|
3
|
+
import { CodeGenerator } from "skir-internal";
|
|
4
|
+
import * as javaGen from "skir-java-gen";
|
|
5
|
+
import * as kotlinGen from "skir-kotlin-gen";
|
|
6
|
+
import * as pythonGen from "skir-python-gen";
|
|
7
|
+
import * as typescriptGen from "skir-typescript-gen";
|
|
8
|
+
import { LineCounter, parseDocument, Scalar, YAMLMap } from "yaml";
|
|
9
|
+
import { SkirConfig } from "./config.js";
|
|
10
|
+
|
|
11
|
+
export interface SkirConfigResult {
|
|
12
|
+
skirConfig: SkirConfig | undefined;
|
|
13
|
+
errors: readonly SkirConfigError[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface SkirConfigError {
|
|
17
|
+
message: string;
|
|
18
|
+
range?: SkirConfigErrorRange;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface SkirConfigErrorRange {
|
|
22
|
+
start: SkirConfigErrorPos;
|
|
23
|
+
end: SkirConfigErrorPos;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface SkirConfigErrorPos {
|
|
27
|
+
/** 0-based */
|
|
28
|
+
offset: number;
|
|
29
|
+
/** 1-based */
|
|
30
|
+
lineNumber: number;
|
|
31
|
+
/** 2-based */
|
|
32
|
+
colNumber: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function parseSkirConfig(
|
|
36
|
+
yamlCode: string,
|
|
37
|
+
importMods?: "import-mods",
|
|
38
|
+
): Promise<SkirConfigResult> {
|
|
39
|
+
const errors: SkirConfigError[] = [];
|
|
40
|
+
|
|
41
|
+
// 1. Parse YAML into a Document object
|
|
42
|
+
const lineCounter = new LineCounter();
|
|
43
|
+
const doc = parseDocument(yamlCode, { lineCounter });
|
|
44
|
+
|
|
45
|
+
const offsetToPos = (offset: number): SkirConfigErrorPos => {
|
|
46
|
+
const pos = lineCounter.linePos(offset);
|
|
47
|
+
return {
|
|
48
|
+
offset: offset,
|
|
49
|
+
lineNumber: pos.line,
|
|
50
|
+
colNumber: pos.col,
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
const offsetRangeToRange = (
|
|
54
|
+
start: number,
|
|
55
|
+
end: number,
|
|
56
|
+
): SkirConfigErrorRange => ({
|
|
57
|
+
start: offsetToPos(start),
|
|
58
|
+
end: offsetToPos(end),
|
|
59
|
+
});
|
|
60
|
+
const pathToRange = (
|
|
61
|
+
path: readonly PropertyKey[],
|
|
62
|
+
): SkirConfigErrorRange | undefined => {
|
|
63
|
+
const node = doc.getIn(path, true) as Scalar | YAMLMap | undefined;
|
|
64
|
+
if (!node || !node.range) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
return offsetRangeToRange(node.range[0], node.range[1]);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// Check for YAML parsing errors
|
|
71
|
+
if (doc.errors.length > 0) {
|
|
72
|
+
for (const error of doc.errors) {
|
|
73
|
+
const range = offsetRangeToRange(error.pos[0], error.pos[1]);
|
|
74
|
+
errors.push({
|
|
75
|
+
message: error.message,
|
|
76
|
+
range: range,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return { skirConfig: undefined, errors: errors };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const jsData = doc.toJS();
|
|
83
|
+
|
|
84
|
+
// 2. Validate with Zod schema
|
|
85
|
+
const result = SkirConfig.safeParse(jsData);
|
|
86
|
+
|
|
87
|
+
if (!result.success) {
|
|
88
|
+
for (const issue of result.error.issues) {
|
|
89
|
+
// Map the Zod path to the YAML node
|
|
90
|
+
const range = pathToRange(issue.path);
|
|
91
|
+
errors.push({
|
|
92
|
+
message: issue.message,
|
|
93
|
+
range: range,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return { skirConfig: undefined, errors: errors };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 3. Validate each generator's config with Zod schema
|
|
100
|
+
for (let i = 0; i < result.data.generators.length; i++) {
|
|
101
|
+
const generatorConfig = result.data.generators[i]!;
|
|
102
|
+
const { mod } = generatorConfig;
|
|
103
|
+
let generator: CodeGenerator<unknown> | undefined;
|
|
104
|
+
if (importMods) {
|
|
105
|
+
try {
|
|
106
|
+
generator = await importCodeGenerator(mod);
|
|
107
|
+
} catch (e) {
|
|
108
|
+
if (e instanceof Error) {
|
|
109
|
+
const range = pathToRange(["generators", i, "mod"]);
|
|
110
|
+
errors.push({
|
|
111
|
+
message: e.message,
|
|
112
|
+
range: range,
|
|
113
|
+
});
|
|
114
|
+
continue;
|
|
115
|
+
} else {
|
|
116
|
+
throw e;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
} else {
|
|
120
|
+
// TODO: rm the casts
|
|
121
|
+
const modToGenerator: Record<string, CodeGenerator<unknown>> = {
|
|
122
|
+
"skir-cc-gen": ccGen.GENERATOR as any as CodeGenerator<unknown>,
|
|
123
|
+
"skir-dart-gen": dartGen.GENERATOR as any as CodeGenerator<unknown>,
|
|
124
|
+
"skir-java-gen": javaGen.GENERATOR as any as CodeGenerator<unknown>,
|
|
125
|
+
"skir-kotlin-gen": kotlinGen.GENERATOR as any as CodeGenerator<unknown>,
|
|
126
|
+
"skir-python-gen": pythonGen.GENERATOR as any as CodeGenerator<unknown>,
|
|
127
|
+
"skir-typescript-gen":
|
|
128
|
+
typescriptGen.GENERATOR as any as CodeGenerator<unknown>,
|
|
129
|
+
};
|
|
130
|
+
generator = modToGenerator[mod];
|
|
131
|
+
}
|
|
132
|
+
if (generator) {
|
|
133
|
+
const parsedGeneratorConfig = generator.configType.safeParse(
|
|
134
|
+
generatorConfig.config,
|
|
135
|
+
);
|
|
136
|
+
if (!parsedGeneratorConfig.success) {
|
|
137
|
+
for (const issue of parsedGeneratorConfig.error.issues) {
|
|
138
|
+
const path: readonly PropertyKey[] = [
|
|
139
|
+
"generators",
|
|
140
|
+
i,
|
|
141
|
+
"config",
|
|
142
|
+
...issue.path,
|
|
143
|
+
];
|
|
144
|
+
const range = pathToRange(path);
|
|
145
|
+
errors.push({
|
|
146
|
+
message: issue.message ?? "Error",
|
|
147
|
+
range: range,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (errors.length > 0) {
|
|
154
|
+
return { skirConfig: undefined, errors: errors };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return { skirConfig: result.data, errors: [] };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export async function importCodeGenerator(
|
|
161
|
+
mod: string,
|
|
162
|
+
): Promise<CodeGenerator<unknown>> {
|
|
163
|
+
const module = await import(mod);
|
|
164
|
+
const generator = module.GENERATOR;
|
|
165
|
+
if (typeof generator !== "object") {
|
|
166
|
+
throw new Error(`Cannot import GENERATOR from module ${mod}`);
|
|
167
|
+
}
|
|
168
|
+
return generator as CodeGenerator<unknown>;
|
|
169
|
+
}
|
|
@@ -3,39 +3,58 @@ import type {
|
|
|
3
3
|
Doc,
|
|
4
4
|
DocPiece,
|
|
5
5
|
DocReference,
|
|
6
|
+
DocReferenceName,
|
|
7
|
+
MutableDoc,
|
|
8
|
+
MutableDocPiece,
|
|
9
|
+
MutableDocReferenceName,
|
|
6
10
|
Result,
|
|
7
11
|
SkirError,
|
|
8
12
|
Token,
|
|
9
13
|
} from "skir-internal";
|
|
10
14
|
|
|
11
|
-
export function
|
|
12
|
-
const parser = new
|
|
15
|
+
export function parseDocComment(docComment: Token): Result<Doc> {
|
|
16
|
+
const parser = new DocCommentParser(docComment);
|
|
13
17
|
return parser.parse();
|
|
14
18
|
}
|
|
15
19
|
|
|
16
|
-
class
|
|
20
|
+
class DocCommentParser {
|
|
17
21
|
private readonly pieces: DocPiece[] = [];
|
|
18
22
|
private readonly errors: SkirError[] = [];
|
|
19
23
|
private currentText = "";
|
|
20
|
-
private docCommentIndex = -1;
|
|
21
24
|
private charIndex = -1;
|
|
22
|
-
private
|
|
23
|
-
|
|
24
|
-
constructor(private readonly
|
|
25
|
+
private readonly content: string;
|
|
26
|
+
|
|
27
|
+
constructor(private readonly docComment: Token) {
|
|
28
|
+
const { text } = docComment;
|
|
29
|
+
if (text.startsWith("/// ")) {
|
|
30
|
+
this.content = text.slice(4);
|
|
31
|
+
} else if (text.startsWith("///")) {
|
|
32
|
+
this.content = text.slice(3);
|
|
33
|
+
} else {
|
|
34
|
+
throw new Error("Expected doc comment to start with ///");
|
|
35
|
+
}
|
|
36
|
+
this.charIndex = 0;
|
|
37
|
+
}
|
|
25
38
|
|
|
26
39
|
parse(): Result<Doc> {
|
|
27
|
-
|
|
28
|
-
this.parseCurrentDocComment();
|
|
29
|
-
}
|
|
40
|
+
this.parseDocComment();
|
|
30
41
|
|
|
31
42
|
// Add any remaining text
|
|
32
43
|
if (this.currentText.length > 0) {
|
|
33
44
|
this.pieces.push({ kind: "text", text: this.currentText });
|
|
34
45
|
}
|
|
35
46
|
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
.
|
|
47
|
+
const { pieces } = this;
|
|
48
|
+
const text = pieces
|
|
49
|
+
.map((p) => {
|
|
50
|
+
switch (p.kind) {
|
|
51
|
+
case "text":
|
|
52
|
+
return p.text;
|
|
53
|
+
case "reference":
|
|
54
|
+
return p.referenceRange.text;
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
.join("");
|
|
39
58
|
|
|
40
59
|
return {
|
|
41
60
|
result: {
|
|
@@ -46,7 +65,7 @@ class DocCommentsParser {
|
|
|
46
65
|
};
|
|
47
66
|
}
|
|
48
67
|
|
|
49
|
-
private
|
|
68
|
+
private parseDocComment(): void {
|
|
50
69
|
// Matches unescaped [ or ], OR escaped [[ or ]]
|
|
51
70
|
const specialCharRegex = /\[\[|\]\]|\[|\]/g;
|
|
52
71
|
|
|
@@ -93,18 +112,15 @@ class DocCommentsParser {
|
|
|
93
112
|
this.charIndex++;
|
|
94
113
|
}
|
|
95
114
|
}
|
|
96
|
-
|
|
97
|
-
// Add newline between comment lines (except after the last line)
|
|
98
|
-
if (this.docCommentIndex < this.docComments.length - 1) {
|
|
99
|
-
this.currentText += "\n";
|
|
100
|
-
}
|
|
101
115
|
}
|
|
102
116
|
|
|
103
117
|
private parseReference(): DocReference {
|
|
104
118
|
const { content, docComment } = this;
|
|
105
119
|
|
|
106
120
|
const leftBracketCharIndex = this.charIndex;
|
|
107
|
-
const
|
|
121
|
+
const contentOffset = docComment.text.length - content.length;
|
|
122
|
+
const startPosition =
|
|
123
|
+
docComment.position + contentOffset + leftBracketCharIndex;
|
|
108
124
|
|
|
109
125
|
const rightBracketCharIndex = content.indexOf("]", leftBracketCharIndex);
|
|
110
126
|
|
|
@@ -139,7 +155,8 @@ class DocCommentsParser {
|
|
|
139
155
|
const tokens: Token[] = [];
|
|
140
156
|
while (this.charIndex < endCharIndex) {
|
|
141
157
|
const char = content[this.charIndex]!;
|
|
142
|
-
const
|
|
158
|
+
const contentOffset = docComment.text.length - content.length;
|
|
159
|
+
const position = docComment.position + contentOffset + this.charIndex;
|
|
143
160
|
|
|
144
161
|
const makeToken = (text: string): Token => ({
|
|
145
162
|
text: text,
|
|
@@ -166,7 +183,9 @@ class DocCommentsParser {
|
|
|
166
183
|
this.charIndex++;
|
|
167
184
|
} else {
|
|
168
185
|
// Invalid character in reference (including whitespace)
|
|
169
|
-
const
|
|
186
|
+
const contentOffset = docComment.text.length - content.length;
|
|
187
|
+
const column =
|
|
188
|
+
this.docComment.colNumber + contentOffset + this.charIndex;
|
|
170
189
|
hasError = true;
|
|
171
190
|
this.errors.push({
|
|
172
191
|
token: referenceRange,
|
|
@@ -177,11 +196,11 @@ class DocCommentsParser {
|
|
|
177
196
|
}
|
|
178
197
|
}
|
|
179
198
|
|
|
180
|
-
const
|
|
199
|
+
const nameParts = hasError ? [] : this.parseNameParts(tokens);
|
|
181
200
|
|
|
182
201
|
return {
|
|
183
202
|
kind: "reference",
|
|
184
|
-
|
|
203
|
+
nameParts: nameParts,
|
|
185
204
|
absolute: tokens[0]?.text === ".",
|
|
186
205
|
referee: undefined,
|
|
187
206
|
docComment: this.docComment,
|
|
@@ -189,8 +208,10 @@ class DocCommentsParser {
|
|
|
189
208
|
};
|
|
190
209
|
}
|
|
191
210
|
|
|
192
|
-
private
|
|
193
|
-
|
|
211
|
+
private parseNameParts(
|
|
212
|
+
tokens: readonly Token[],
|
|
213
|
+
): readonly DocReferenceName[] {
|
|
214
|
+
const nameParts: MutableDocReferenceName[] = [];
|
|
194
215
|
let expect: "identifier" | "identifier or '.'" | "'.' or ']'" =
|
|
195
216
|
"identifier or '.'";
|
|
196
217
|
for (const token of tokens) {
|
|
@@ -198,7 +219,10 @@ class DocCommentsParser {
|
|
|
198
219
|
if (/^[a-zA-Z]/.test(token.text)) {
|
|
199
220
|
expected = expect === "identifier or '.'" || expect === "identifier";
|
|
200
221
|
expect = "'.' or ']'";
|
|
201
|
-
|
|
222
|
+
nameParts.push({
|
|
223
|
+
token: token,
|
|
224
|
+
declaration: undefined,
|
|
225
|
+
});
|
|
202
226
|
} else if (token.text === ".") {
|
|
203
227
|
expected = expect === "identifier or '.'" || expect === "'.' or ']'";
|
|
204
228
|
expect = "identifier";
|
|
@@ -214,38 +238,38 @@ class DocCommentsParser {
|
|
|
214
238
|
return [];
|
|
215
239
|
}
|
|
216
240
|
if (token.text === "]") {
|
|
217
|
-
return
|
|
241
|
+
return nameParts;
|
|
218
242
|
}
|
|
219
243
|
}
|
|
220
244
|
// An error has already been pushed to signify the unterminated reference.
|
|
221
245
|
return [];
|
|
222
246
|
}
|
|
247
|
+
}
|
|
223
248
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
return
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/// The text of the current doc comment being parsed.
|
|
230
|
-
private get content(): string {
|
|
231
|
-
return this.docComment.text;
|
|
249
|
+
export function mergeDocs(docs: readonly Doc[]): MutableDoc {
|
|
250
|
+
if (docs.length <= 0) {
|
|
251
|
+
return EMPTY_DOC;
|
|
232
252
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}
|
|
243
|
-
throw new Error("Expected doc comment to start with ///");
|
|
244
|
-
}
|
|
245
|
-
this.charIndex = this.contentOffset;
|
|
246
|
-
return true;
|
|
247
|
-
} else {
|
|
248
|
-
return false;
|
|
253
|
+
// Insert '\n' between each doc comment (== line)
|
|
254
|
+
const text = docs.map((d) => d.text).join("\n");
|
|
255
|
+
const pieces: MutableDocPiece[] = [];
|
|
256
|
+
for (let i = 0; i < docs.length; ++i) {
|
|
257
|
+
const doc = docs[i]!;
|
|
258
|
+
if (i !== 0) {
|
|
259
|
+
pieces.push({
|
|
260
|
+
kind: "text",
|
|
261
|
+
text: "\n",
|
|
262
|
+
});
|
|
249
263
|
}
|
|
264
|
+
doc.pieces.forEach((p) => pieces.push(p));
|
|
250
265
|
}
|
|
266
|
+
return {
|
|
267
|
+
text: text,
|
|
268
|
+
pieces: pieces,
|
|
269
|
+
};
|
|
251
270
|
}
|
|
271
|
+
|
|
272
|
+
const EMPTY_DOC: Doc = {
|
|
273
|
+
text: "",
|
|
274
|
+
pieces: [],
|
|
275
|
+
};
|
package/src/error_renderer.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
Expression,
|
|
12
12
|
getTokenForBreakingChange,
|
|
13
13
|
} from "./compatibility_checker.js";
|
|
14
|
+
import { SkirConfigError } from "./config_parser.js";
|
|
14
15
|
import { ModuleSet } from "./module_set.js";
|
|
15
16
|
|
|
16
17
|
export function renderErrors(errors: readonly SkirError[]): void {
|
|
@@ -56,6 +57,39 @@ export function formatError(error: SkirError): string {
|
|
|
56
57
|
return result;
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
export function renderSkirConfigErrors(
|
|
61
|
+
errors: readonly SkirConfigError[],
|
|
62
|
+
context: {
|
|
63
|
+
skirConfigPath: string;
|
|
64
|
+
},
|
|
65
|
+
): void {
|
|
66
|
+
for (const error of errors) {
|
|
67
|
+
console.error();
|
|
68
|
+
console.error(formatSkirConfigError(error, context));
|
|
69
|
+
}
|
|
70
|
+
console.error();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function formatSkirConfigError(
|
|
74
|
+
error: SkirConfigError,
|
|
75
|
+
context: {
|
|
76
|
+
skirConfigPath: string;
|
|
77
|
+
},
|
|
78
|
+
): string {
|
|
79
|
+
const { message, range } = error;
|
|
80
|
+
const { skirConfigPath } = context;
|
|
81
|
+
const location = range
|
|
82
|
+
? [
|
|
83
|
+
makeCyan(skirConfigPath),
|
|
84
|
+
// Already 1-based
|
|
85
|
+
makeYellow(range.start.lineNumber.toString()),
|
|
86
|
+
// Already 1-based
|
|
87
|
+
makeYellow(range.start.colNumber.toString()),
|
|
88
|
+
].join(":")
|
|
89
|
+
: makeCyan(skirConfigPath);
|
|
90
|
+
return [location, " - ", makeRed("error"), ": ", message].join("");
|
|
91
|
+
}
|
|
92
|
+
|
|
59
93
|
export function renderBreakingChanges(
|
|
60
94
|
breakingChanges: readonly BreakingChange[],
|
|
61
95
|
moduleSet: BeforeAfter<ModuleSet>,
|