@plugjs/tsgo 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,209 @@
1
+ import { assert } from '@plugjs/plug/asserts';
2
+ import { $gry, $p, $ylw, ERROR } from '@plugjs/plug/logging';
3
+ import { assertAbsolutePath, getAbsoluteParent, resolveFile } from '@plugjs/plug/paths';
4
+ import { convertProjectDiagnostics } from './diagnostics.js';
5
+ /* ========================================================================== */
6
+ /**
7
+ * Find the TypeScript project configuration file for a given path.
8
+ *
9
+ * The path specified can be a `tsconfig.json` file name or a directory
10
+ * containing a `tsconfig.json` file.
11
+ *
12
+ * The returned path is the absolute path of the actual `tsconfig.json` file.
13
+ */
14
+ export function findProjectConfig(path) {
15
+ // The specified path can be either a file or a directory containing a
16
+ // "tsconfig.json" file: resolve as a file, then assume it's a directory
17
+ let file = resolveFile(path);
18
+ if (!file)
19
+ file = resolveFile(path, 'tsconfig.json');
20
+ assert(file, `TypeScript configuration file not found in "${path}"`);
21
+ return file;
22
+ }
23
+ /**
24
+ * Read the TypeScript project configuration file for a given path.
25
+ *
26
+ * The path specified can be a `tsconfig.json` file name or a directory
27
+ * containing a `tsconfig.json` file.
28
+ *
29
+ * The returned object contains the resolved configuration file name (in `path`)
30
+ * and the array of resolved project references (the actual resolved files on
31
+ * disk in `references`).
32
+ *
33
+ * Any errors encountered while reading the configuration file or resolving its
34
+ * references are returned in the `errors` array.
35
+ */
36
+ export async function readProjectConfig(api, path) {
37
+ const file = findProjectConfig(path);
38
+ // Use the TypeScript API to load up this project... We already update the
39
+ // snapshot here, so we don't need to do it again once we end up compiling
40
+ // the whole project...
41
+ const snapshot = await api.updateSnapshot({ openProjects: [file] });
42
+ const project = snapshot.getProject(file);
43
+ assert(project, `Failed to open TypeScript project from "${file}"`);
44
+ assertAbsolutePath(project.configFileName);
45
+ path = project.configFileName; // rewrite the path to the actual
46
+ // Get any diagnostics encountered while parsing the configuration file
47
+ // and any "global" diagnostics (not associated with a specific file)...
48
+ const diagnostics = await Promise.all([
49
+ project.program.getGlobalDiagnostics(),
50
+ project.program.getProgramDiagnostics(),
51
+ project.program.getConfigFileParsingDiagnostics(),
52
+ ]).then((results) => results.flat());
53
+ // Convert the diagnostics to our simplified format
54
+ const errors = await convertProjectDiagnostics(diagnostics, project.program, path);
55
+ // Prepare our simplified result object
56
+ const config = project.parsedCommandLine;
57
+ const result = { path, errors, config, references: [] };
58
+ // Resolve the references to absolute paths
59
+ const directory = getAbsoluteParent(path);
60
+ for (const reference of config.projectReferences ?? []) {
61
+ let resolved = resolveFile(directory, reference.path);
62
+ if (!resolved)
63
+ resolved = resolveFile(directory, reference.path, 'tsconfig.json');
64
+ if (resolved)
65
+ result.references.push(resolved);
66
+ }
67
+ // All done
68
+ return result;
69
+ }
70
+ /**
71
+ * Find _all_ project references given a set of initial project paths.
72
+ *
73
+ * The returned map contains all the projects (resolved configuration file
74
+ * names) and their referring projects as values.
75
+ */
76
+ export async function findProjectReferences(api, ...paths) {
77
+ // The set of *all* projects, whether we have resolved them yet or not.
78
+ const remainingProjects = new Set(paths);
79
+ // The set of all *resolved* projects (that is, opened and parsed)
80
+ const resolvedProjects = new Set();
81
+ // The map of all our projects, keyed by their resolved configuration file
82
+ // name and having the set of projects that refer to it as a value
83
+ const projects = new Map();
84
+ // An array of all errors encountered while reading the configuration files
85
+ const errors = [];
86
+ // Add a referring project to the set of referring projects for a referred project
87
+ function addProject(referredProject, referringProject) {
88
+ let referringProjects = projects.get(referredProject);
89
+ if (!referringProjects) {
90
+ referringProjects = new Set();
91
+ projects.set(referredProject, referringProjects);
92
+ }
93
+ if (referringProject)
94
+ referringProjects.add(referringProject);
95
+ }
96
+ while (remainingProjects.size > 0) {
97
+ // Take and remove the next project from the remaining set
98
+ const project = remainingProjects.values().next().value;
99
+ remainingProjects.delete(project);
100
+ // Read the project configuration file and its references
101
+ const { path, references, errors: e } = await readProjectConfig(api, project);
102
+ // If we already resolved this project, skip it
103
+ if (resolvedProjects.has(path))
104
+ continue;
105
+ // Push any errors encountered while reading the configuration file
106
+ errors.push(...e);
107
+ // Add the project (resolved filename) to the resolved set
108
+ resolvedProjects.add(path);
109
+ // Record the project and its references in our map of projects
110
+ addProject(path);
111
+ // Look at all the project references
112
+ for (const reference of references) {
113
+ // If we haven't resolved this reference yet, add it to the remaining
114
+ if (!resolvedProjects.has(reference))
115
+ remainingProjects.add(reference);
116
+ // Then make sure that we record the referring project for this reference
117
+ addProject(reference, path);
118
+ }
119
+ }
120
+ // Return our projects map and all errors
121
+ return { projects, errors };
122
+ }
123
+ /**
124
+ * Use Kahn's topological-sort algorithm on a map of projects and their
125
+ * references to determine the order in which they should be built.
126
+ *
127
+ * @param projects A map of projects and their referring projects
128
+ * @returns The order of projects, any unresolved projects, and cycles found
129
+ */
130
+ export function resolveProjectOrder(projects) {
131
+ // The number of unresolved dependencies for each project
132
+ const counts = Object.create(null);
133
+ // Iterate through all the projects (and their referring projects), counting
134
+ // the number of unresolved dependencies for each project.
135
+ for (const [referred, referring] of projects.entries()) {
136
+ // Ensure the referred project has a dependencies count (even if it is zero)
137
+ counts[referred] = counts[referred] ?? 0;
138
+ // For each referring project, increment its count of unresolved dependencies
139
+ for (const ref of referring)
140
+ counts[ref] = (counts[ref] ?? 0) + 1;
141
+ }
142
+ // The unique set of all known projects (referred and referring)
143
+ const uniques = Object.keys(counts);
144
+ // Projects are "ready" when all dependencies have been resolved (zero count)
145
+ const ready = uniques.filter((project) => counts[project] === 0);
146
+ // The final order of projects (in dependency order)
147
+ const order = [];
148
+ // While there are still projects ready to be processed, take the first one
149
+ for (let project = ready.shift(); project; project = ready.shift()) {
150
+ // Add the project to the final order
151
+ order.push(project);
152
+ // For each project that depends on this project, decrement its unresolved
153
+ // dependency count. If it reaches zero, add it to the ready queue.
154
+ for (const dependent of projects.get(project) ?? /* coverage ignore next */ []) {
155
+ const remaining = (counts[dependent] = counts[dependent] - 1);
156
+ if (remaining === 0)
157
+ ready.push(dependent);
158
+ }
159
+ }
160
+ // When no more projects are "ready", the remaining projects are unresolved
161
+ const unresolved = uniques.filter((project) => counts[project]);
162
+ // If we have no unresolved projects, we can return the order (no cycles)
163
+ if (unresolved.length === 0)
164
+ return { order, unresolved, cycles: [], errors: [] };
165
+ // ===== FIND CYCLES =========================================================
166
+ // Projects we _already_ visited (and thus don't need to visit again)
167
+ const visited = new Set();
168
+ // The list of cycles we have found (each cycle is a list of projects)
169
+ const cycles = [];
170
+ // Recursively visit a project's dependents, looking for cycles
171
+ function visit(project, stack = []) {
172
+ // If this project is already in the current path, we found a cycle
173
+ const index = stack.indexOf(project);
174
+ if (index >= 0) {
175
+ cycles.push([...stack.slice(index), project]);
176
+ return;
177
+ }
178
+ // Don't revisit projects whose dependents have all been processed
179
+ if (visited.has(project))
180
+ return;
181
+ // Add this project to the current path and visit its dependents
182
+ const next = [...stack, project];
183
+ for (const dependent of projects.get(project) ?? /* coverage ignore next */ []) {
184
+ if (unresolved.includes(dependent))
185
+ visit(dependent, next);
186
+ }
187
+ // Mark the project as visited after all its dependents have been processed
188
+ visited.add(project);
189
+ }
190
+ // Visit all the unresolved projects, looking for cycles
191
+ for (const project of unresolved)
192
+ visit(project);
193
+ // Prep our errors
194
+ const errors = [];
195
+ errors.push({
196
+ level: ERROR,
197
+ message: `Found ${$ylw(unresolved.length)} unresolved projects (${$ylw(cycles.length)} cycles)` +
198
+ `\n${$gry('-')} ${unresolved.map($p).join(`\n${$gry('-')} `)}`,
199
+ });
200
+ for (const cycle of cycles) {
201
+ errors.push({
202
+ level: ERROR,
203
+ message: `Project reference cycle:\n${$gry('-')} ${cycle.map($p).join(`\n${$gry('-')} `)}`,
204
+ });
205
+ }
206
+ // Return the final order, unresolved projects, and cycles found
207
+ return { order, unresolved, cycles, errors };
208
+ }
209
+ //# sourceMappingURL=projects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects.js","sourceRoot":"","sources":["../src/projects.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import { Files } from '@plugjs/plug/files';
2
+ import type { TscOptions, TypeScriptOptions } from './index.ts';
3
+ import type { Pipe } from '@plugjs/plug';
4
+ import type { Context, Plug } from '@plugjs/plug/pipe';
5
+ export declare class TypeScript implements Plug<Files> {
6
+ private _emitOnly;
7
+ private _reportDeprecations;
8
+ private _projectReferences;
9
+ constructor(options?: TypeScriptOptions);
10
+ pipe(files: Files, context: Context): Promise<Files>;
11
+ }
12
+ /**
13
+ * Compile the project identified by the specified configuration file
14
+ * (either a `tsconfig.json` or a directory containing one).
15
+ *
16
+ * This is roughly equivalent to the command line `tsc --project <config>`.
17
+ */
18
+ export declare function tsc(config: string, options?: TscOptions): Pipe;
19
+ /**
20
+ * Compile the project identified by the specified configuration file
21
+ * (either a `tsconfig.json` or a directory containing one) and all of its
22
+ * references.
23
+ *
24
+ * This is roughly equivalent to the command line `tsc --build <config>`.
25
+ */
26
+ export declare function tsbuild(config: string, options?: TscOptions): Pipe;
27
+ //# sourceMappingURL=typescript.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typescript.d.ts","sourceRoot":"","sources":["../src/typescript.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAQ1C,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAC/D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AAExC,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAA;AAOtD,qBAAa,UAAW,YAAW,IAAI,CAAC,KAAK,CAAC;IAC5C,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,mBAAmB,CAA2B;IACtD,OAAO,CAAC,kBAAkB,CAAS;IAEnC,YAAY,OAAO,GAAE,iBAAsB,EA2C1C;IAEK,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAqGzD;CACF;AAMD;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,GAAG,IAAI,CAGlE;AAID;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,GAAG,IAAI,CAGtE"}
@@ -0,0 +1,176 @@
1
+ import { using } from '@plugjs/plug';
2
+ import { assert } from '@plugjs/plug/asserts';
3
+ import { Files } from '@plugjs/plug/files';
4
+ import { ERROR } from '@plugjs/plug/logging';
5
+ import { commonPath, getAbsoluteParent, resolveAbsolutePath } from '@plugjs/plug/paths';
6
+ import { API, EmitOnly } from 'typescript/unstable/async';
7
+ import { convertDiagnostics } from './diagnostics.js';
8
+ import { findProjectConfig, findProjectReferences, readProjectConfig, resolveProjectOrder } from './projects.js';
9
+ /* ========================================================================== *
10
+ * TYPESCRIPT 7 PLUG IMPLEMENTATION *
11
+ * ========================================================================== */
12
+ export class TypeScript {
13
+ _emitOnly;
14
+ _reportDeprecations;
15
+ _projectReferences;
16
+ constructor(options = {}) {
17
+ const { projectReferences, reportDeprecations, emitOnly } = options;
18
+ this._projectReferences = !!projectReferences;
19
+ switch (emitOnly) {
20
+ // If *none* or *false* we don't emit any files at all
21
+ case 'none':
22
+ case false:
23
+ this._emitOnly = null;
24
+ break;
25
+ // If *js* we only emit JavaScript files
26
+ case 'js':
27
+ this._emitOnly = EmitOnly.OnlyJs;
28
+ break;
29
+ // If *dts* we only emit declaration files
30
+ case 'dts':
31
+ this._emitOnly = EmitOnly.OnlyDts;
32
+ break;
33
+ // If *all* or *true* (default) we emit all files
34
+ case 'all':
35
+ case true:
36
+ default:
37
+ this._emitOnly = EmitOnly.All;
38
+ break;
39
+ }
40
+ switch (reportDeprecations) {
41
+ // If *warn* we report deprecations as warnings
42
+ case 'warn':
43
+ this._reportDeprecations = 'warn';
44
+ break;
45
+ // If *off* or *false* we don't report deprecations at all
46
+ case 'off':
47
+ case false:
48
+ this._reportDeprecations = 'off';
49
+ break;
50
+ // If *notice* or *true* (default) we report deprecations as notices
51
+ case 'notice':
52
+ case true:
53
+ default:
54
+ this._reportDeprecations = 'notice';
55
+ break;
56
+ }
57
+ }
58
+ async pipe(files, context) {
59
+ // Create our TypeScript API and report for this plug
60
+ const api = new API({ cwd: files.directory });
61
+ const report = context.log.report('TypeScript Report');
62
+ // This is the list of emitted files to return to the pipe...
63
+ const emittedFiles = [];
64
+ try {
65
+ // The list of projects to build (in order) and the snapshot used
66
+ // to build them.
67
+ let projects;
68
+ let snapshot;
69
+ // First of all figure out whether we have to build project references
70
+ // too or not... This will determine whether we have to resolve them....
71
+ if (this._projectReferences) {
72
+ // Find all project references for the given configuration files
73
+ const projectReferences = await findProjectReferences(api, ...files.absolutePaths());
74
+ report.add(...projectReferences.errors);
75
+ if (report.errors)
76
+ report.done();
77
+ // Then determine the build order for all projects and their references
78
+ const projectOrder = resolveProjectOrder(projectReferences.projects);
79
+ report.add(...projectOrder.errors);
80
+ if (report.errors)
81
+ report.done();
82
+ // At this point, our list of projects to build is the "order"
83
+ snapshot = await api.updateSnapshot({ openProjects: projectOrder.order });
84
+ projects = [...projectOrder.order];
85
+ }
86
+ else {
87
+ // If we're not building references, then we just build our files
88
+ const configs = [...files.absolutePaths()].map(findProjectConfig);
89
+ snapshot = await api.updateSnapshot({ openProjects: configs });
90
+ projects = [...configs];
91
+ // When resolving the project references above, we already processed
92
+ // any configuration files and added any errors to the report...
93
+ // In this case, we have to do it ourselves by reading each config!
94
+ for (const config of configs) {
95
+ const { errors } = await readProjectConfig(api, config);
96
+ report.add(...errors);
97
+ }
98
+ // If we have any errors at this point, we can't continue...
99
+ if (report.errors)
100
+ report.done();
101
+ }
102
+ // Now build all projects in the order we determined earlier
103
+ for (const project of projects) {
104
+ // Get the actual TypeScript program for this project...
105
+ const program = snapshot.getProject(project)?.program;
106
+ assert(program, `Project "${project}" not found in snapshot`);
107
+ // Collect compilation diagnostics for this project (has files)
108
+ const diagnostics = await Promise.all([
109
+ program.getBindDiagnostics(),
110
+ program.getSemanticDiagnostics(),
111
+ program.getSyntacticDiagnostics(),
112
+ program.getSuggestionDiagnostics(),
113
+ program.getDeclarationDiagnostics(),
114
+ ]).then((results) => results.flat());
115
+ // When emitting, let's emit the files
116
+ if (this._emitOnly !== null) {
117
+ // Emit the files for this program and collect diagnostics
118
+ const emitted = await program.emit(this._emitOnly);
119
+ diagnostics.push(...emitted.diagnostics);
120
+ // Make sure that we add any emitted file to the list to be returned
121
+ if (emitted.emittedFiles?.length) {
122
+ const directory = getAbsoluteParent(project);
123
+ const mapper = resolveAbsolutePath.bind(null, directory);
124
+ emittedFiles.push(...emitted.emittedFiles.map((file) => mapper(file)));
125
+ }
126
+ // Report an error if emitting was skipped for some reason (we should
127
+ // have some diagnostics associated with this, but still...)
128
+ // coverage ignore if // don't know how to test this...
129
+ if (emitted.emitSkipped) {
130
+ report.add({
131
+ level: ERROR,
132
+ message: `Unable to emit files for this project`,
133
+ file: project,
134
+ });
135
+ }
136
+ }
137
+ // Add all diagnostics to the report
138
+ report.add(...(await convertDiagnostics(diagnostics, program, this._reportDeprecations)));
139
+ }
140
+ }
141
+ finally {
142
+ await api.close();
143
+ }
144
+ report.done();
145
+ const outputPath = commonPath(files.directory, ...emittedFiles);
146
+ return Files.builder(outputPath)
147
+ .add(...emittedFiles)
148
+ .build();
149
+ }
150
+ }
151
+ /* ========================================================================== *
152
+ * TYPESCRIPT 7 HELPERS *
153
+ * ========================================================================== */
154
+ /**
155
+ * Compile the project identified by the specified configuration file
156
+ * (either a `tsconfig.json` or a directory containing one).
157
+ *
158
+ * This is roughly equivalent to the command line `tsc --project <config>`.
159
+ */
160
+ export function tsc(config, options = {}) {
161
+ const { directory, ...opts } = options;
162
+ return using(config, { directory }).plug(new TypeScript({ ...opts, projectReferences: false }));
163
+ }
164
+ /* ========================================================================== */
165
+ /**
166
+ * Compile the project identified by the specified configuration file
167
+ * (either a `tsconfig.json` or a directory containing one) and all of its
168
+ * references.
169
+ *
170
+ * This is roughly equivalent to the command line `tsc --build <config>`.
171
+ */
172
+ export function tsbuild(config, options = {}) {
173
+ const { directory, ...opts } = options;
174
+ return using(config, { directory }).plug(new TypeScript({ ...opts, projectReferences: true }));
175
+ }
176
+ //# sourceMappingURL=typescript.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typescript.js","sourceRoot":"","sources":["../src/typescript.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@plugjs/tsgo",
3
+ "version": "0.0.0",
4
+ "description": "TypeScript 7.1 support for PlugJS",
5
+ "keywords": [
6
+ "build",
7
+ "compile",
8
+ "ecmascript",
9
+ "javascript",
10
+ "node",
11
+ "plugjs",
12
+ "typescript"
13
+ ],
14
+ "homepage": "https://github.com/plugjs/tsgo#readme",
15
+ "bugs": {
16
+ "url": "https://github.com/plugjs/tsgo/issues"
17
+ },
18
+ "license": "Apache-2.0",
19
+ "author": "Juit Developers <developers@juit.com>",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/plugjs/tsgo.git"
23
+ },
24
+ "directories": {
25
+ "test": "test"
26
+ },
27
+ "files": [
28
+ "*.md",
29
+ "dist/",
30
+ "src/"
31
+ ],
32
+ "type": "module",
33
+ "main": "./dist/index.js",
34
+ "types": "./dist/index.d.ts",
35
+ "exports": {
36
+ ".": {
37
+ "types": "./dist/index.d.ts",
38
+ "default": "./dist/index.js"
39
+ },
40
+ "./typescript": {
41
+ "types": "./dist/typescript.d.ts",
42
+ "default": "./dist/typescript.js"
43
+ }
44
+ },
45
+ "scripts": {
46
+ "build": "plug",
47
+ "coverage": "plug coverage",
48
+ "dev": "plug coverage -w src -w test",
49
+ "lint": "plug lint",
50
+ "lintfix": "plug lint fix=true",
51
+ "test": "plug test",
52
+ "transpile": "plug transpile"
53
+ },
54
+ "dependencies": {
55
+ "typescript": "^7.1.0-dev"
56
+ },
57
+ "devDependencies": {
58
+ "@plugjs/cov8": "^0.7.3",
59
+ "@plugjs/expect5": "^0.7.3",
60
+ "@plugjs/oxc": "^0.1.3",
61
+ "@plugjs/plug": "^0.7.3"
62
+ },
63
+ "peerDependencies": {
64
+ "@plugjs/plug": "^0.7.3"
65
+ }
66
+ }
@@ -0,0 +1,123 @@
1
+ import { ERROR, NOTICE, WARN } from '@plugjs/plug/logging'
2
+ import { assertAbsolutePath } from '@plugjs/plug/paths'
3
+ import { DiagnosticCategory } from 'typescript/unstable/async'
4
+
5
+ import type { ReportRecord } from '@plugjs/plug/logging'
6
+ import type { AbsolutePath } from '@plugjs/plug/paths'
7
+ import type { SourceFile } from 'typescript/unstable/ast'
8
+ import type { Diagnostic, Program } from 'typescript/unstable/async'
9
+
10
+ function convertDiagnostic(diagnostic: Diagnostic, sourceFile?: SourceFile): ReportRecord {
11
+ const record: ReportRecord & { tags: string[] } = {
12
+ level: NOTICE,
13
+ message: diagnostic.text,
14
+ tags: [`ts${diagnostic.code}`],
15
+ }
16
+
17
+ switch (diagnostic.category) {
18
+ case DiagnosticCategory.Error:
19
+ record.level = ERROR
20
+ break
21
+ case DiagnosticCategory.Warning:
22
+ record.level = WARN
23
+ break
24
+ }
25
+
26
+ if (diagnostic.reportsDeprecated) record.tags.push('deprecated')
27
+ if (diagnostic.reportsUnnecessary) record.tags.push('unnecessary')
28
+
29
+ if (diagnostic.fileName) {
30
+ assertAbsolutePath(diagnostic.fileName)
31
+ record.file = diagnostic.fileName
32
+ }
33
+
34
+ if (sourceFile) {
35
+ const pos = sourceFile.getLineAndCharacterOfPosition(diagnostic.pos)
36
+ record.line = pos.line + 1
37
+ record.column = pos.character + 1
38
+ record.length = diagnostic.end - diagnostic.pos
39
+ record.source = sourceFile.getFullText()
40
+ }
41
+
42
+ return record
43
+ }
44
+
45
+ export async function convertDiagnostics(
46
+ diagnostics: readonly Diagnostic[],
47
+ program: Program,
48
+ deprecations: 'warn' | 'notice' | 'off' = 'notice',
49
+ ): Promise<ReportRecord[]> {
50
+ // Filter out any duplicate diagnostic *before* we convert them to report
51
+ // records (that might be expensive because of source file lookups)
52
+ const seen = new Set<string>()
53
+ const unique = diagnostics.filter((diagnostic) => {
54
+ const key = JSON.stringify([
55
+ diagnostic.category,
56
+ diagnostic.code,
57
+ diagnostic.text,
58
+ diagnostic.pos,
59
+ diagnostic.end,
60
+ diagnostic.fileName,
61
+ ])
62
+ if (seen.has(key)) return false
63
+ seen.add(key)
64
+ return true
65
+ })
66
+
67
+ // Convert all diagnostics to report records
68
+ const converted = await Promise.all(
69
+ unique.map(async (diagnostic) => {
70
+ // Anything not an error is not critical
71
+ const isNonCritical = diagnostic.category !== DiagnosticCategory.Error
72
+
73
+ // First of all see how we need to handle non-error deprecations: when
74
+ // "off" we suppress them, when "warn" we convert them to warnings...
75
+ if (diagnostic.reportsDeprecated && isNonCritical) {
76
+ if (deprecations === 'off') return // simply filter this out...
77
+ if (deprecations === 'warn') {
78
+ diagnostic = { ...diagnostic, category: DiagnosticCategory.Warning }
79
+ }
80
+ }
81
+
82
+ // First figure out where this diagnostic came from...
83
+ const sourceFileMetadata = diagnostic.fileName
84
+ ? await program.getSourceFileMetadata(diagnostic.fileName)
85
+ : /* coverage ignore next */ undefined
86
+
87
+ // If the source file is in the default library (e.g. "lib.dom.ts") or
88
+ // from an external library (in "node_modules") then we ONLY keep errors
89
+ // and (we ignore things like warnings, suggestions, ...)
90
+ const isExternal = sourceFileMetadata?.isDefaultLibrary || sourceFileMetadata?.isFromExternalLibrary
91
+ if (isExternal && isNonCritical) return
92
+
93
+ // Get the source file and its metadata (if any) for the diagnostic...
94
+ // We do this *after* the external check above as getting the source file
95
+ // is quite expensive (especially for things like "lib.es5.dts")...
96
+ const sourceFile = diagnostic.fileName
97
+ ? ((await program.getSourceFile(diagnostic.fileName)) ??
98
+ (await program.getConfigSourceFile(diagnostic.fileName)))
99
+ : /* coverage ignore next */ undefined
100
+
101
+ // Convert our diagnostic to a report record and return it
102
+ return convertDiagnostic(diagnostic, sourceFile)
103
+ }),
104
+ )
105
+
106
+ // Filter out any null/undefined records
107
+ return converted.filter((record): record is ReportRecord => !!record)
108
+ }
109
+
110
+ export async function convertProjectDiagnostics(
111
+ diagnostics: readonly Diagnostic[],
112
+ program: Program,
113
+ fileName: AbsolutePath,
114
+ ): Promise<ReportRecord[]> {
115
+ // Make sure that we have a file name for the diagnostics
116
+ const fileDiagnostics = diagnostics.map((diagnostic) => {
117
+ if (diagnostic.fileName) return diagnostic
118
+ return { ...diagnostic, fileName }
119
+ })
120
+
121
+ // Convert the diagnostics to report records and return them
122
+ return convertDiagnostics(fileDiagnostics, program)
123
+ }