@atlaspack/diagnostic 2.14.2-typescript-8a6ec6c8b.0 → 2.14.2-typescript-d6e6d169c.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,270 @@
1
+ // @flow
2
+
3
+ import type { Mapping } from "@mischnic/json-sourcemap";
4
+
5
+ /**
6
+ * These positions are 1-based (so <code>1</code> is the first line/column)
7
+ */
8
+ export type DiagnosticHighlightLocation = {|
9
+ +line: number,
10
+ +column: number,
11
+ |};
12
+
13
+ export type DiagnosticSeverity = "error" | "warn" | "info";
14
+ /**
15
+ * Note: A tab character is always counted as a single character
16
+ * This is to prevent any mismatch of highlighting across machines
17
+ */
18
+
19
+ export type DiagnosticCodeHighlight = {|
20
+ /**
21
+ * Location of the first character that should get highlighted for this highlight.
22
+ */
23
+ start: DiagnosticHighlightLocation,
24
+
25
+ /**
26
+ * Location of the last character that should get highlighted for this highlight.
27
+ */
28
+ end: DiagnosticHighlightLocation,
29
+
30
+ /**
31
+ * A message that should be displayed at this location in the code (optional).
32
+ */
33
+ message?: string,
34
+ |};
35
+
36
+ /**
37
+ * Describes how to format a code frame.
38
+ * A code frame is a visualization of a piece of code with a certain amount of
39
+ * code highlights that point to certain chunk(s) inside the code.
40
+ */
41
+ export type DiagnosticCodeFrame = {|
42
+ /**
43
+ * The contents of the source file.
44
+ *
45
+ * If no code is passed, it will be read in from filePath, remember that
46
+ * the asset's current code could be different from the input contents.
47
+ */
48
+ code?: string,
49
+
50
+ /**
51
+ * Path to the file this code frame is about (optional, absolute or relative to the project root)
52
+ */
53
+ filePath?: string,
54
+
55
+ /**
56
+ * Language of the file this code frame is about (optional)
57
+ */
58
+ language?: string,
59
+ codeHighlights: Array<DiagnosticCodeHighlight>,
60
+ |};
61
+
62
+ /**
63
+ * A JSON object (as in "map")
64
+ */
65
+ export type JSONObject = {
66
+ [key: string]: any,
67
+ };
68
+ /**
69
+ * A style agnostic way of emitting errors, warnings and info.
70
+ * Reporters are responsible for rendering the message, codeframes, hints, ...
71
+ */
72
+
73
+ export type Diagnostic = {|
74
+ /**
75
+ * This is the message you want to log.
76
+ */
77
+ message: string,
78
+
79
+ /**
80
+ * Name of plugin or file that threw this error
81
+ */
82
+ origin?: string,
83
+
84
+ /**
85
+ * A stacktrace of the error (optional)
86
+ */
87
+ stack?: string,
88
+
89
+ /**
90
+ * Name of the error (optional)
91
+ */
92
+ name?: string,
93
+
94
+ /**
95
+ * A code frame points to a certain location(s) in the file this diagnostic is linked to (optional)
96
+ */
97
+ codeFrames?: ?Array<DiagnosticCodeFrame>,
98
+
99
+ /**
100
+ * An optional list of strings that suggest ways to resolve this issue
101
+ */
102
+ hints?: Array<string>,
103
+
104
+ /**
105
+ * @private
106
+ */
107
+ skipFormatting?: boolean,
108
+
109
+ /**
110
+ * A URL to documentation to learn more about the diagnostic.
111
+ */
112
+ documentationURL?: string,
113
+
114
+ /**
115
+ * Diagnostic specific metadata (optional)
116
+ */
117
+ meta?: JSONObject,
118
+ |};
119
+
120
+ export interface PrintableError extends Error {
121
+ fileName?: string;
122
+ filePath?: string;
123
+ codeFrame?: string;
124
+ highlightedCodeFrame?: string;
125
+ loc?: ?{
126
+ column: number,
127
+ line: number,
128
+ ...
129
+ },
130
+ source?: string;
131
+ }
132
+
133
+ export type DiagnosticWithoutOrigin = {|
134
+ ...Diagnostic,
135
+ origin?: string,
136
+ |};
137
+
138
+ /**
139
+ * Something that can be turned into a diagnostic.
140
+ */
141
+ export type Diagnostifiable =
142
+ | Diagnostic
143
+ | Array<Diagnostic>
144
+ | ThrowableDiagnostic
145
+ | PrintableError
146
+ | Error
147
+ | string;
148
+
149
+ /**
150
+ * Normalize the given value into a diagnostic.
151
+ */
152
+ declare export function anyToDiagnostic(
153
+ input: Diagnostifiable
154
+ ): Array<Diagnostic>;
155
+
156
+ /**
157
+ * Normalize the given error into a diagnostic.
158
+ */
159
+ declare export function errorToDiagnostic(
160
+ error: ThrowableDiagnostic | PrintableError | string,
161
+ defaultValues?: {
162
+ origin?: string,
163
+ filePath?: string,
164
+ ...
165
+ }
166
+ ): Array<Diagnostic>;
167
+
168
+ export type ThrowableDiagnosticOpts = {
169
+ diagnostic: Diagnostic | Array<Diagnostic>,
170
+ ...
171
+ };
172
+
173
+ /**
174
+ * An error wrapper around a diagnostic that can be <code>throw</code>n (e.g. to signal a
175
+ * build error).
176
+ */
177
+ declare export default class ThrowableDiagnostic mixins Error {
178
+ diagnostics: Array < Diagnostic >;
179
+ constructor(opts: ThrowableDiagnosticOpts): this;
180
+ }
181
+
182
+ /**
183
+ * Turns a list of positions in a JSON5 file with messages into a list of diagnostics.
184
+ * Uses <a href="https://github.com/mischnic/json-sourcemap">@mischnic/json-sourcemap</a>.
185
+ * @param code the JSON code
186
+ * @param ids A list of JSON keypaths (<code>key: "/some/parent/child"</code>) with corresponding messages, \
187
+ * <code>type</code> signifies whether the key of the value in a JSON object should be highlighted.
188
+ */
189
+ declare export function generateJSONCodeHighlights(
190
+ data:
191
+ | string
192
+ | {|
193
+ data: mixed,
194
+ pointers: {| [key: string]: Mapping |},
195
+ |},
196
+ ids: Array<{|
197
+ key: string,
198
+ type?: ?"key" | "value",
199
+ message?: string,
200
+ |}>
201
+ ): Array < DiagnosticCodeHighlight >;
202
+
203
+ /**
204
+ * Converts entries in <a href="https://github.com/mischnic/json-sourcemap">@mischnic/json-sourcemap</a>'s
205
+ * <code>result.pointers</code> array.
206
+ */
207
+ declare export function getJSONHighlightLocation(
208
+ pos: Mapping,
209
+ type?: "key" | "value"
210
+ ): {|
211
+ start: DiagnosticHighlightLocation,
212
+ end: DiagnosticHighlightLocation,
213
+ |};
214
+
215
+ /**
216
+ * Result is 1-based, but end is exclusive
217
+ */
218
+ declare export function getJSONSourceLocation(
219
+ pos: Mapping,
220
+ type?: ?"key" | "value"
221
+ ): {|
222
+ +start: {|
223
+ +line: number,
224
+ +column: number,
225
+ |},
226
+ +end: {|
227
+ +line: number,
228
+ +column: number,
229
+ |},
230
+ |};
231
+
232
+ declare export function convertSourceLocationToHighlight<
233
+ Location: {
234
+ /**
235
+ * 1-based, inclusive
236
+ */
237
+ +start: {|
238
+ +line: number,
239
+ +column: number,
240
+ |},
241
+
242
+ /**
243
+ * 1-based, exclusive
244
+ */
245
+ +end: {|
246
+ +line: number,
247
+ +column: number,
248
+ |},
249
+ ...
250
+ }
251
+ > (
252
+ x: Location,
253
+ message ?: string
254
+ ): DiagnosticCodeHighlight;
255
+
256
+ /**
257
+ * Sanitizes object keys before using them as <code>key</code> in generateJSONCodeHighlights
258
+ */
259
+ declare export function encodeJSONKeyComponent(component: string): string;
260
+ declare export function escapeMarkdown(s: string): string;
261
+ export type TemplateInput = $FlowFixMe;
262
+ export interface MdFunction {
263
+ (strings: Array<string>, ...params: Array<TemplateInput>): string;
264
+ bold: (s: TemplateInput) => TemplateInput;
265
+ italic: (s: TemplateInput) => TemplateInput;
266
+ underline: (s: TemplateInput) => TemplateInput;
267
+ strikethrough: (s: TemplateInput) => TemplateInput;
268
+ }
269
+
270
+ export var md: MdFunction;
package/lib/index.mjs ADDED
@@ -0,0 +1,199 @@
1
+ import assert from 'node:assert';
2
+ import nullthrows from 'nullthrows';
3
+ import * as jsonSourcemap from '@mischnic/json-sourcemap';
4
+ /** Normalize the given value into a diagnostic. */
5
+ export function anyToDiagnostic(input) {
6
+ if (Array.isArray(input)) {
7
+ return input.flatMap((e) => anyToDiagnostic(e));
8
+ } else if (input instanceof ThrowableDiagnostic) {
9
+ return input.diagnostics;
10
+ } else if (input instanceof Error) {
11
+ return errorToDiagnostic(input);
12
+ } else if (typeof input === 'string') {
13
+ return [{message: input}];
14
+ } else if (typeof input === 'object') {
15
+ return [input];
16
+ } else {
17
+ return errorToDiagnostic(input);
18
+ }
19
+ }
20
+ /** Normalize the given error into a diagnostic. */
21
+ export function errorToDiagnostic(error, defaultValues) {
22
+ let codeFrames = undefined;
23
+ if (typeof error === 'string') {
24
+ return [
25
+ {
26
+ origin: defaultValues?.origin ?? 'Error',
27
+ message: escapeMarkdown(error),
28
+ },
29
+ ];
30
+ }
31
+ if (error instanceof ThrowableDiagnostic) {
32
+ return error.diagnostics.map((d) => {
33
+ return {
34
+ ...d,
35
+ origin: d.origin ?? defaultValues?.origin ?? 'unknown',
36
+ };
37
+ });
38
+ }
39
+ if (error.loc && error.source != null) {
40
+ codeFrames = [
41
+ {
42
+ filePath:
43
+ error.filePath ??
44
+ error.fileName ??
45
+ defaultValues?.filePath ??
46
+ undefined,
47
+ code: error.source,
48
+ codeHighlights: [
49
+ {
50
+ start: {
51
+ line: error.loc.line,
52
+ column: error.loc.column,
53
+ },
54
+ end: {
55
+ line: error.loc.line,
56
+ column: error.loc.column,
57
+ },
58
+ },
59
+ ],
60
+ },
61
+ ];
62
+ }
63
+ return [
64
+ {
65
+ origin: defaultValues?.origin ?? 'Error',
66
+ message: escapeMarkdown(error.message),
67
+ name: error.name,
68
+ stack:
69
+ codeFrames == null
70
+ ? error.highlightedCodeFrame ?? error.codeFrame ?? error.stack
71
+ : undefined,
72
+ codeFrames,
73
+ },
74
+ ];
75
+ }
76
+ /**
77
+ * An error wrapper around a diagnostic that can be <code>throw</code>n (e.g. to signal a
78
+ * build error).
79
+ */
80
+ export default class ThrowableDiagnostic extends Error {
81
+ diagnostics;
82
+ constructor(opts) {
83
+ let diagnostics = Array.isArray(opts.diagnostic)
84
+ ? opts.diagnostic
85
+ : [opts.diagnostic];
86
+ // Construct error from diagnostics
87
+ super(diagnostics[0].message);
88
+ // @ts-ignore
89
+ this.stack = diagnostics[0].stack ?? super.stack;
90
+ // @ts-ignore
91
+ this.name = diagnostics[0].name ?? super.name;
92
+ this.diagnostics = diagnostics;
93
+ }
94
+ }
95
+ /**
96
+ * Turns a list of positions in a JSON5 file with messages into a list of diagnostics.
97
+ * Uses <a href="https://github.com/mischnic/json-sourcemap">@mischnic/json-sourcemap</a>.
98
+ *
99
+ * @param code the JSON code
100
+ * @param ids A list of JSON keypaths (<code>key: "/some/parent/child"</code>) with corresponding messages, \
101
+ * <code>type</code> signifies whether the key of the value in a JSON object should be highlighted.
102
+ */
103
+ export function generateJSONCodeHighlights(data, ids) {
104
+ let map =
105
+ typeof data == 'string'
106
+ ? jsonSourcemap.default.parse(data, undefined, {
107
+ dialect: 'JSON5',
108
+ tabWidth: 1,
109
+ })
110
+ : data;
111
+ return ids.map(({key, type, message}) => {
112
+ let pos = nullthrows.default(map.pointers[key]);
113
+ return {
114
+ ...getJSONHighlightLocation(pos, type),
115
+ message,
116
+ };
117
+ });
118
+ }
119
+ /**
120
+ * Converts entries in <a href="https://github.com/mischnic/json-sourcemap">@mischnic/json-sourcemap</a>'s
121
+ * <code>result.pointers</code> array.
122
+ */
123
+ export function getJSONHighlightLocation(pos, type) {
124
+ let key = 'key' in pos ? pos.key : undefined;
125
+ let keyEnd = 'keyEnd' in pos ? pos.keyEnd : undefined;
126
+ if (!type && key && pos.value) {
127
+ // key and value
128
+ return {
129
+ start: {line: key.line + 1, column: key.column + 1},
130
+ end: {line: pos.valueEnd.line + 1, column: pos.valueEnd.column},
131
+ };
132
+ } else if (type == 'key' || !pos.value) {
133
+ assert(key && keyEnd);
134
+ return {
135
+ start: {line: key.line + 1, column: key.column + 1},
136
+ end: {line: keyEnd.line + 1, column: keyEnd.column},
137
+ };
138
+ } else {
139
+ return {
140
+ start: {line: pos.value.line + 1, column: pos.value.column + 1},
141
+ end: {line: pos.valueEnd.line + 1, column: pos.valueEnd.column},
142
+ };
143
+ }
144
+ }
145
+ /** Result is 1-based, but end is exclusive */
146
+ export function getJSONSourceLocation(pos, type) {
147
+ let v = getJSONHighlightLocation(pos, type);
148
+ return {start: v.start, end: {line: v.end.line, column: v.end.column + 1}};
149
+ }
150
+ export function convertSourceLocationToHighlight({start, end}, message) {
151
+ return {message, start, end: {line: end.line, column: end.column - 1}};
152
+ }
153
+ /** Sanitizes object keys before using them as <code>key</code> in generateJSONCodeHighlights */
154
+ export function encodeJSONKeyComponent(component) {
155
+ return component.replace(/~/g, '~0').replace(/\//g, '~1');
156
+ }
157
+ const escapeCharacters = ['\\', '*', '_', '~'];
158
+ export function escapeMarkdown(s) {
159
+ let result = s;
160
+ for (const char of escapeCharacters) {
161
+ result = result.replace(new RegExp(`\\${char}`, 'g'), `\\${char}`);
162
+ }
163
+ return result;
164
+ }
165
+ const mdVerbatim = Symbol();
166
+ export const md = function (strings, ...params) {
167
+ let result = [];
168
+ for (let i = 0; i < params.length; i++) {
169
+ result.push(strings[i]);
170
+ let param = params[i];
171
+ if (Array.isArray(param)) {
172
+ for (let j = 0; j < param.length; j++) {
173
+ result.push(param[j]?.[mdVerbatim] ?? escapeMarkdown(`${param[j]}`));
174
+ if (j < param.length - 1) {
175
+ result.push(', ');
176
+ }
177
+ }
178
+ } else {
179
+ result.push(param?.[mdVerbatim] ?? escapeMarkdown(`${param}`));
180
+ }
181
+ }
182
+ return result.join('') + strings[strings.length - 1];
183
+ };
184
+ md.bold = function (s) {
185
+ // $FlowFixMe[invalid-computed-prop]
186
+ return {[mdVerbatim]: '**' + escapeMarkdown(`${s}`) + '**'};
187
+ };
188
+ md.italic = function (s) {
189
+ // $FlowFixMe[invalid-computed-prop]
190
+ return {[mdVerbatim]: '_' + escapeMarkdown(`${s}`) + '_'};
191
+ };
192
+ md.underline = function (s) {
193
+ // $FlowFixMe[invalid-computed-prop]
194
+ return {[mdVerbatim]: '__' + escapeMarkdown(`${s}`) + '__'};
195
+ };
196
+ md.strikethrough = function (s) {
197
+ // $FlowFixMe[invalid-computed-prop]
198
+ return {[mdVerbatim]: '~~' + escapeMarkdown(`${s}`) + '~~'};
199
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaspack/diagnostic",
3
- "version": "2.14.2-typescript-8a6ec6c8b.0",
3
+ "version": "2.14.2-typescript-d6e6d169c.0",
4
4
  "description": "Types and utilities for printing source-code located errors, warning and information messages.",
5
5
  "license": "(MIT OR Apache-2.0)",
6
6
  "publishConfig": {
@@ -10,20 +10,30 @@
10
10
  "type": "git",
11
11
  "url": "https://github.com/atlassian-labs/atlaspack.git"
12
12
  },
13
- "main": "lib/diagnostic.js",
14
- "source": "src/diagnostic.js",
15
- "types": "lib/diagnostic.d.ts",
13
+ "main": "lib/index.js",
14
+ "types": "lib/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "atlaspack::sources": "./src/index.mts",
18
+ "types": [
19
+ "./lib/index.d.ts",
20
+ "./src/index.mts"
21
+ ],
22
+ "import": "./lib/index.mjs",
23
+ "require": "./lib/index.js",
24
+ "default": "./lib/index.js"
25
+ },
26
+ "./*": "./*"
27
+ },
16
28
  "engines": {
17
29
  "node": ">= 16.0.0"
18
30
  },
19
31
  "scripts": {
20
- "build-ts": "flow-to-ts src/*.js --write && tsc --emitDeclarationOnly --declaration --esModuleInterop src/*.ts && mkdir -p lib && mv src/*.d.ts lib/. && rm src/*.ts",
21
- "check-ts": "tsc --noEmit lib/diagnostic.d.ts"
32
+ "build-tsc": "node ../../../scripts/build-tsc.mjs"
22
33
  },
23
34
  "dependencies": {
24
35
  "@mischnic/json-sourcemap": "^0.1.0",
25
36
  "nullthrows": "^1.1.1"
26
37
  },
27
- "type": "commonjs",
28
- "gitHead": "8a6ec6c8bed0bbf5461f5b0f899577a576906051"
38
+ "gitHead": "d6e6d169cb16a0bbc98d7743646a7097b82c7fe2"
29
39
  }