metar-taf-parser 0.0.2 → 1.0.2

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/LICENSE CHANGED
@@ -1,6 +1,7 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2021 Alexander Harding
3
+ Copyright (c) 2022 Alexander Harding
4
+ Copyright 2021 Jean-Kevin KPADEY
4
5
 
5
6
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
7
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,42 +1,36 @@
1
- # metar-taf-parser
1
+ # ✈️ [metar-taf-parser](https://aeharding.github.io/metar-taf-parser)
2
2
 
3
- This is a port of [python-metar-taf-parser](https://github.com/mivek/python-metar-taf-parser) to typescript. It's fully typed and tested with i18n support, and can run on Node or the browser.
3
+ This is a port of [python-metar-taf-parser](https://github.com/mivek/python-metar-taf-parser) to Typescript. It's fully typed and tested with i18n support, and can run on Node or the browser.
4
4
 
5
- ## Example
5
+ [Check out the demo here](https://aeharding.github.io/metar-taf-parser)
6
6
 
7
- ⚠️ This project is an active work in progress. These examples do not currently work.
7
+ ## Installation
8
8
 
9
- ### Parse METAR
9
+ ```sh
10
+ yarn add metar-taf-parser
11
+ # or
12
+ npm i --save metar-taf-parser
13
+ ```
10
14
 
11
- ```ts
12
- import { parseMetar } from 'metar-taf-parser'
15
+ ## Usage
13
16
 
14
- // Get the raw METAR/TAF strings in your preferred way
15
- // For example: https://www.aviationweather.gov/dataserver
16
- const { metar } = await myService.getAirportData('KMSN')
17
+ ### Parse a METAR
17
18
 
18
- // Readily serializable
19
- const metarResult = parseMetar(metar)
19
+ ```ts
20
+ import { parseMetar } from 'metar-taf-parser'
20
21
 
21
- // Your code here 🚀
22
+ const metar = parseMetar(rawMetarString)
22
23
  ```
23
24
 
24
- ### Parse TAF
25
+ ### Parse a TAF
25
26
 
26
27
  ```ts
27
28
  import { parseTAF } from 'metar-taf-parser'
28
29
 
29
- // Get the raw METAR/TAF strings in your preferred way
30
- // For example: https://www.aviationweather.gov/dataserver
31
- const { taf } = await myService.getAirportData('KMSN')
32
-
33
- // Readily serializable
34
- const tafResult = parseTAF(taf)
35
-
36
- // Your code here 🚀
30
+ const taf = parseTAF(rawTAFString)
37
31
  ```
38
32
 
39
- ### i18n
33
+ ## i18n
40
34
 
41
35
  ```ts
42
36
  import { parseMetar } from 'metar-taf-parser'
@@ -51,4 +45,12 @@ const metarResult = parseMetar(metar, { locale: de })
51
45
 
52
46
  ### Example site
53
47
 
54
- Please see [the example site README.md](example/README.md).
48
+ Please see [the example site README.md](example/README.md).
49
+
50
+ ## Contributing
51
+
52
+ This project is intended to provide feature parity with [python-metar-taf-parser](https://github.com/mivek/python-metar-taf-parser) and will only accept PRs to maintain feature parity or to fix inconsistencies with that project.
53
+
54
+ ## Acknowledgment
55
+
56
+ This software port was made possible due to the fantastic work of [@mivek](https://github.com/mivek) in [python-metar-taf-parser](https://github.com/mivek/python-metar-taf-parser). If you like this project, please [consider buying @mivek a coffee](https://ko-fi.com/mivek).
@@ -135,6 +135,8 @@ class WindVariationCommand {
135
135
  wind.maxVariation = +matches[2];
136
136
  }
137
137
  execute(container, windString) {
138
+ if (!container.wind)
139
+ throw new errors_1.InvalidWeatherStatementError();
138
140
  this.parseWindVariation(container.wind, windString);
139
141
  return true;
140
142
  }
@@ -158,7 +158,7 @@ class HourlyPressureCommand extends Command {
158
158
  const matches = code.match(__classPrivateFieldGet(this, _HourlyPressureCommand_regex, "f"));
159
159
  if (!matches)
160
160
  throw new errors_1.UnexpectedParseError("Match not found");
161
- remark.push(`${(0, i18n_1._)(`Remark.Barometer.${+matches[1]}`, this.locale)} ${(0, i18n_1.format)((0, i18n_1._)("Remark.Pressure.Tendency", this.locale), +matches[1])}`);
161
+ remark.push(`${(0, i18n_1._)(`Remark.Barometer.${+matches[1]}`, this.locale)} ${(0, i18n_1.format)((0, i18n_1._)("Remark.Pressure.Tendency", this.locale), +matches[2] / 10)}`);
162
162
  return [code.replace(__classPrivateFieldGet(this, _HourlyPressureCommand_regex, "f"), "").trim(), remark];
163
163
  }
164
164
  }
@@ -2,5 +2,8 @@ export declare function degreesToCardinal(input: number | string): string;
2
2
  export declare function convertVisibility(input: string): string;
3
3
  export declare function convertTemperature(input: string): number;
4
4
  export declare function convertInchesMercuryToPascal(input: number): number;
5
- export declare function convertTemperatureRemarks(sign: string, temperature: string): number;
5
+ /**
6
+ * Converts number `.toFixed(1)` before outputting to match python implementation
7
+ */
8
+ export declare function convertTemperatureRemarks(sign: string, temperature: string): string;
6
9
  export declare function convertPrecipitationAmount(amount: string): number;
@@ -44,11 +44,14 @@ function convertInchesMercuryToPascal(input) {
44
44
  return 33.8639 * input;
45
45
  }
46
46
  exports.convertInchesMercuryToPascal = convertInchesMercuryToPascal;
47
+ /**
48
+ * Converts number `.toFixed(1)` before outputting to match python implementation
49
+ */
47
50
  function convertTemperatureRemarks(sign, temperature) {
48
51
  const temp = +temperature / 10;
49
52
  if (sign === "0")
50
- return temp;
51
- return -temp;
53
+ return temp.toFixed(1);
54
+ return (-temp).toFixed(1);
52
55
  }
53
56
  exports.convertTemperatureRemarks = convertTemperatureRemarks;
54
57
  function convertPrecipitationAmount(amount) {
package/dist/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
- import { IMetar } from "./model/model";
1
+ import { IMetar, ITAF } from "./model/model";
2
2
  import { Locale } from "./commons/i18n";
3
- export interface IMetarTAFParserOptions {
4
- locale?: Locale;
5
- }
6
- export declare function parseMetar(metar: string, options: IMetarTAFParserOptions): IMetar;
7
3
  export { Locale } from "./commons/i18n";
8
4
  export * from "./commons/errors";
9
5
  export * from "./model/model";
10
6
  export * from "./model/enum";
7
+ export interface IMetarTAFParserOptions {
8
+ locale?: Locale;
9
+ }
10
+ export declare function parseMetar(metar: string, options?: IMetarTAFParserOptions): IMetar;
11
+ export declare function parseTAF(taf: string, options?: IMetarTAFParserOptions): ITAF;
package/dist/index.js CHANGED
@@ -17,14 +17,25 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
17
17
  return (mod && mod.__esModule) ? mod : { "default": mod };
18
18
  };
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.parseMetar = void 0;
20
+ exports.parseTAF = exports.parseMetar = void 0;
21
21
  const parser_1 = require("./parser/parser");
22
22
  const errors_1 = require("./commons/errors");
23
23
  const en_1 = __importDefault(require("./locale/en"));
24
+ __exportStar(require("./commons/errors"), exports);
25
+ __exportStar(require("./model/model"), exports);
26
+ __exportStar(require("./model/enum"), exports);
24
27
  function parseMetar(metar, options) {
28
+ return parse(metar, options, parser_1.MetarParser);
29
+ }
30
+ exports.parseMetar = parseMetar;
31
+ function parseTAF(taf, options) {
32
+ return parse(taf, options, parser_1.TAFParser);
33
+ }
34
+ exports.parseTAF = parseTAF;
35
+ function parse(report, options, parser) {
25
36
  const lang = (options === null || options === void 0 ? void 0 : options.locale) || en_1.default;
26
37
  try {
27
- return new parser_1.MetarParser(lang).parse(metar);
38
+ return new parser(lang).parse(report);
28
39
  }
29
40
  catch (e) {
30
41
  if (e instanceof errors_1.ParseError)
@@ -32,7 +43,3 @@ function parseMetar(metar, options) {
32
43
  throw new errors_1.InvalidWeatherStatementError(e);
33
44
  }
34
45
  }
35
- exports.parseMetar = parseMetar;
36
- __exportStar(require("./commons/errors"), exports);
37
- __exportStar(require("./model/model"), exports);
38
- __exportStar(require("./model/enum"), exports);
@@ -55,12 +55,12 @@ export interface ICloud {
55
55
  type?: CloudType;
56
56
  }
57
57
  export interface IAbstractWeatherContainer {
58
- wind: IWind;
58
+ wind?: IWind;
59
59
  visibility?: Visibility;
60
- verticalVisibility: number;
61
- windShear: IWindShear;
62
- cavok: boolean;
63
- remark: string;
60
+ verticalVisibility?: number;
61
+ windShear?: IWindShear;
62
+ cavok?: boolean;
63
+ remark?: string;
64
64
  remarks: string[];
65
65
  clouds: ICloud[];
66
66
  weatherConditions: IWeatherCondition[];
@@ -78,25 +78,25 @@ export interface ITime {
78
78
  }
79
79
  export interface IAbstractWeatherCode extends IAbstractWeatherContainer, ITime {
80
80
  day: number;
81
- airport: IAirport;
81
+ airport?: IAirport;
82
82
  message: string;
83
83
  station: string;
84
84
  trends: IAbstractTrend[];
85
85
  }
86
86
  export interface IMetar extends IAbstractWeatherCode {
87
- temperature: number;
88
- dewPoint: number;
89
- altimeter: number;
87
+ temperature?: number;
88
+ dewPoint?: number;
89
+ altimeter?: number;
90
90
  nosig: boolean;
91
91
  auto: boolean;
92
92
  runwaysInfo: IRunwayInfo[];
93
93
  trends: IMetarTrend[];
94
94
  }
95
95
  export interface ITAF extends IAbstractWeatherCode {
96
- validity: IAbstractValidity;
97
- maxTemperature: number;
98
- minTemperature: number;
99
- amendment: boolean;
96
+ validity: IValidity;
97
+ maxTemperature?: ITemperatureDated;
98
+ minTemperature?: ITemperatureDated;
99
+ amendment?: boolean;
100
100
  trends: ITAFTrend[];
101
101
  }
102
102
  export interface IAbstractTrend extends IAbstractWeatherContainer {
@@ -109,13 +109,15 @@ export interface IMetarTrend extends IAbstractTrend {
109
109
  times: IMetarTrendTime[];
110
110
  }
111
111
  export interface ITAFTrend extends IAbstractTrend {
112
- probability: number;
113
- validity: IAbstractValidity;
112
+ probability?: number;
113
+ validity?: IFMValidity;
114
114
  }
115
- export interface IValidity extends IAbstractValidity {
115
+ export interface IEndValidity {
116
116
  endHour: number;
117
117
  endDay: number;
118
118
  }
119
- export interface IFMValidity extends IAbstractValidity {
120
- startMinutes: number;
119
+ export interface IValidity extends IAbstractValidity, IEndValidity {
120
+ }
121
+ export interface IFMValidity extends IAbstractValidity, Partial<IEndValidity> {
122
+ startMinutes?: number;
121
123
  }
@@ -1,4 +1,5 @@
1
- import { IAbstractWeatherContainer, IMetar, IMetarTrend, ITemperatureDated, IValidity, IWeatherCondition } from "../model/model";
1
+ import { IAbstractWeatherContainer, IMetar, IMetarTrend, ITAF, ITAFTrend, ITemperatureDated, IValidity, IWeatherCondition } from "../model/model";
2
+ import { WeatherChangeType } from "../model/enum";
2
3
  import { Locale } from "../commons/i18n";
3
4
  /**
4
5
  * Parses the temperature in a TAF
@@ -18,10 +19,12 @@ export declare function parseValidity(input: string): IValidity;
18
19
  */
19
20
  export declare abstract class AbstractParser {
20
21
  #private;
22
+ protected locale: Locale;
21
23
  FM: string;
22
24
  TEMPO: string;
23
25
  BECMG: string;
24
26
  RMK: string;
27
+ constructor(locale: Locale);
25
28
  parseWeatherCondition(input: string): IWeatherCondition;
26
29
  /**
27
30
  * Parses the message into different tokens
@@ -39,10 +42,8 @@ export declare abstract class AbstractParser {
39
42
  }
40
43
  export declare class MetarParser extends AbstractParser {
41
44
  #private;
42
- private locale;
43
45
  AT: string;
44
46
  TL: string;
45
- constructor(locale: Locale);
46
47
  /**
47
48
  * Parses a trend of a metar
48
49
  * @param index the index starting the trend in the list
@@ -58,6 +59,43 @@ export declare class MetarParser extends AbstractParser {
58
59
  */
59
60
  parse(input: string): IMetar;
60
61
  }
62
+ /**
63
+ * Parser for TAF messages
64
+ */
65
+ export declare class TAFParser extends AbstractParser {
66
+ #private;
67
+ TAF: string;
68
+ PROB: string;
69
+ TX: string;
70
+ TN: string;
71
+ /**
72
+ * the message to parse
73
+ * @param input
74
+ * @returns a TAF object
75
+ * @throws ParseError if the message is invalid
76
+ */
77
+ parse(input: string): ITAF;
78
+ /**
79
+ * Format the message as a multiple line code so each line can be parsed
80
+ * @param tafCode The base message
81
+ * @returns a list of string representing the lines of the message
82
+ */
83
+ extractLinesTokens(tafCode: string): string[][];
84
+ /**
85
+ * Parses the tokens of the line and updates the TAF object
86
+ * @param taf TAF object to update
87
+ * @param lineTokens the array of tokens representing a line
88
+ */
89
+ parseLine(taf: ITAF, lineTokens: string[]): void;
90
+ /**
91
+ * Parses a trend of the TAF
92
+ * @param index the index at which the array should be parsed
93
+ * @param line The array of string containing the line
94
+ * @param trend The trend object to update
95
+ */
96
+ parseTrend(index: number, line: string[], trend: ITAFTrend): void;
97
+ makeEmptyTAFTrend(type: WeatherChangeType): ITAFTrend;
98
+ }
61
99
  export declare class RemarkParser {
62
100
  #private;
63
101
  private locale;
@@ -27,9 +27,9 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
27
27
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
28
28
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
29
29
  };
30
- var _AbstractParser_INTENSITY_REGEX, _AbstractParser_CAVOK, _AbstractParser_commonSupplier, _MetarParser_commandSupplier, _RemarkParser_supplier;
30
+ var _AbstractParser_INTENSITY_REGEX, _AbstractParser_CAVOK, _AbstractParser_commonSupplier, _MetarParser_commandSupplier, _TAFParser_validityPattern, _RemarkParser_supplier;
31
31
  Object.defineProperty(exports, "__esModule", { value: true });
32
- exports.RemarkParser = exports.MetarParser = exports.AbstractParser = exports.parseValidity = exports.parseTemperature = void 0;
32
+ exports.RemarkParser = exports.TAFParser = exports.MetarParser = exports.AbstractParser = exports.parseValidity = exports.parseTemperature = void 0;
33
33
  const remark_1 = require("../command/remark");
34
34
  const model_1 = require("../model/model");
35
35
  const converter = __importStar(require("../commons/converter"));
@@ -49,8 +49,6 @@ function parseDeliveryTime(timeString) {
49
49
  hour: +timeString.slice(2, 4),
50
50
  minute: +timeString.slice(4, 6),
51
51
  };
52
- // TODO
53
- // abstractWeatherCode.time = new Date()
54
52
  }
55
53
  /**
56
54
  * This function parses the array containing the remark and concat the array into a string
@@ -109,7 +107,8 @@ function parseFromValidity(input) {
109
107
  * Base parser.
110
108
  */
111
109
  class AbstractParser {
112
- constructor() {
110
+ constructor(locale) {
111
+ this.locale = locale;
113
112
  this.FM = "FM";
114
113
  this.TEMPO = "TEMPO";
115
114
  this.BECMG = "BECMG";
@@ -200,9 +199,8 @@ class AbstractParser {
200
199
  exports.AbstractParser = AbstractParser;
201
200
  _AbstractParser_INTENSITY_REGEX = new WeakMap(), _AbstractParser_CAVOK = new WeakMap(), _AbstractParser_commonSupplier = new WeakMap();
202
201
  class MetarParser extends AbstractParser {
203
- constructor(locale) {
204
- super();
205
- this.locale = locale;
202
+ constructor() {
203
+ super(...arguments);
206
204
  this.AT = "AT";
207
205
  this.TL = "TL";
208
206
  _MetarParser_commandSupplier.set(this, new metar_1.CommandSupplier());
@@ -224,7 +222,6 @@ class MetarParser extends AbstractParser {
224
222
  trendParts[i].startsWith(this.AT)) {
225
223
  const trendTime = {
226
224
  type: enum_1.TimeIndicator[trendParts[i].slice(0, 2)],
227
- // TODO implement time
228
225
  hour: +trendParts[i].slice(2, 4),
229
226
  minute: +trendParts[i].slice(4, 6),
230
227
  };
@@ -244,18 +241,7 @@ class MetarParser extends AbstractParser {
244
241
  */
245
242
  parse(input) {
246
243
  const metarTab = this.tokenize(input);
247
- const metar = Object.assign(Object.assign({}, parseDeliveryTime(metarTab[1])), { station: metarTab[0], message: input,
248
- // wind: IWind,
249
- // visibility?: Visibility,
250
- // verticalVisibility: number,
251
- // windShear: IWindShear,
252
- cavok: false, remark: "", remarks: [], clouds: [], weatherConditions: [],
253
- // airport: IAirport,
254
- trends: [],
255
- // temperature: number,
256
- // dewPoint: number,
257
- // altimeter: number,
258
- nosig: false, auto: false, runwaysInfo: [] }); // TODO remove cast
244
+ const metar = Object.assign(Object.assign({}, parseDeliveryTime(metarTab[1])), { station: metarTab[0], message: input, cavok: false, remark: "", remarks: [], clouds: [], weatherConditions: [], trends: [], nosig: false, auto: false, runwaysInfo: [] });
259
245
  let index = 2;
260
246
  while (index < metarTab.length) {
261
247
  if (!super.generalParse(metar, metarTab[index])) {
@@ -272,8 +258,8 @@ class MetarParser extends AbstractParser {
272
258
  weatherConditions: [],
273
259
  clouds: [],
274
260
  times: [],
261
+ remarks: [],
275
262
  };
276
- // TODO - remove casting
277
263
  index = this.parseTrend(index, trend, metarTab);
278
264
  metar.trends.push(trend);
279
265
  }
@@ -294,6 +280,144 @@ class MetarParser extends AbstractParser {
294
280
  }
295
281
  exports.MetarParser = MetarParser;
296
282
  _MetarParser_commandSupplier = new WeakMap();
283
+ /**
284
+ * Parser for TAF messages
285
+ */
286
+ class TAFParser extends AbstractParser {
287
+ constructor() {
288
+ super(...arguments);
289
+ this.TAF = "TAF";
290
+ this.PROB = "PROB";
291
+ this.TX = "TX";
292
+ this.TN = "TN";
293
+ _TAFParser_validityPattern.set(this, /^\d{4}\/\d{4}$/);
294
+ }
295
+ /**
296
+ * the message to parse
297
+ * @param input
298
+ * @returns a TAF object
299
+ * @throws ParseError if the message is invalid
300
+ */
301
+ parse(input) {
302
+ let amendment;
303
+ const lines = this.extractLinesTokens(input);
304
+ if (lines[0][0] !== this.TAF)
305
+ throw new errors_1.InvalidWeatherStatementError('TAF report must begin with string "TAF"');
306
+ let index = 1;
307
+ if (lines[0][1] === this.TAF)
308
+ index = 2;
309
+ if (lines[0][index] === "AMD") {
310
+ amendment = true;
311
+ index += 1;
312
+ }
313
+ const station = lines[0][index];
314
+ index += 1;
315
+ const time = parseDeliveryTime(lines[0][index]);
316
+ index += 1;
317
+ const validity = parseValidity(lines[0][index]);
318
+ const taf = Object.assign(Object.assign({ station,
319
+ amendment }, time), { validity, message: input, trends: [], remarks: [], clouds: [], weatherConditions: [] });
320
+ for (let i = index + 1; i < lines[0].length; i++) {
321
+ const token = lines[0][i];
322
+ if (token == this.RMK)
323
+ parseRemark(taf, lines[0], i, this.locale);
324
+ else if (token.startsWith(this.TX))
325
+ taf.maxTemperature = parseTemperature(token);
326
+ else if (token.startsWith(this.TN))
327
+ taf.minTemperature = parseTemperature(token);
328
+ else
329
+ this.generalParse(taf, token);
330
+ }
331
+ // Handle the other lines
332
+ for (let i = 1; i < lines.length; i++) {
333
+ this.parseLine(taf, lines[i]);
334
+ }
335
+ return taf;
336
+ }
337
+ /**
338
+ * Format the message as a multiple line code so each line can be parsed
339
+ * @param tafCode The base message
340
+ * @returns a list of string representing the lines of the message
341
+ */
342
+ extractLinesTokens(tafCode) {
343
+ const singleLine = tafCode.replace(/\n/g, " ");
344
+ const cleanLine = singleLine.replace(/\s{2,}/g, " ");
345
+ const lines = joinProbIfNeeded(cleanLine
346
+ .replace(/\s(?=PROB\d{2}\sTEMPO|TEMPO|BECMG|FM|PROB)/g, "\n")
347
+ .split(/\n/));
348
+ // TODO cleanup
349
+ function joinProbIfNeeded(ls) {
350
+ for (let i = 0; i < ls.length; i++) {
351
+ if (/PROB\d{2}/.test(ls[i]) && /TEMPO/.test(ls[i + 1])) {
352
+ ls.splice(i, 2, `${ls[i]} ${ls[i + 1]}`);
353
+ i--;
354
+ }
355
+ }
356
+ return ls;
357
+ }
358
+ const linesToken = lines.map(this.tokenize);
359
+ if (linesToken.length > 1) {
360
+ const lastLine = linesToken[lines.length - 1];
361
+ const temperatures = lastLine.filter((l) => l.startsWith(this.TX) || l.startsWith(this.TN));
362
+ if (temperatures.length) {
363
+ linesToken[0] = linesToken[0].concat(temperatures);
364
+ linesToken[lines.length - 1] = lastLine.filter((l) => !l.startsWith(this.TX) && !l.startsWith(this.TN));
365
+ }
366
+ }
367
+ return linesToken;
368
+ }
369
+ /**
370
+ * Parses the tokens of the line and updates the TAF object
371
+ * @param taf TAF object to update
372
+ * @param lineTokens the array of tokens representing a line
373
+ */
374
+ parseLine(taf, lineTokens) {
375
+ let index = 1;
376
+ let trend;
377
+ if (lineTokens[0].startsWith(this.FM)) {
378
+ trend = Object.assign(Object.assign({}, this.makeEmptyTAFTrend(enum_1.WeatherChangeType.FM)), { validity: parseFromValidity(lineTokens[0]) });
379
+ }
380
+ else if (lineTokens[0].startsWith(this.PROB)) {
381
+ trend = this.makeEmptyTAFTrend(enum_1.WeatherChangeType.PROB);
382
+ if (lineTokens.length > 1 && lineTokens[1] === this.TEMPO) {
383
+ trend = this.makeEmptyTAFTrend(enum_1.WeatherChangeType[lineTokens[1]]);
384
+ index = 2;
385
+ }
386
+ trend.probability = +lineTokens[0].slice(4);
387
+ }
388
+ else {
389
+ trend = this.makeEmptyTAFTrend(enum_1.WeatherChangeType[lineTokens[0]]);
390
+ }
391
+ this.parseTrend(index, lineTokens, trend);
392
+ taf.trends.push(trend);
393
+ }
394
+ /**
395
+ * Parses a trend of the TAF
396
+ * @param index the index at which the array should be parsed
397
+ * @param line The array of string containing the line
398
+ * @param trend The trend object to update
399
+ */
400
+ parseTrend(index, line, trend) {
401
+ for (let i = index; i < line.length; i++) {
402
+ if (line[i] === this.RMK)
403
+ parseRemark(trend, line, i, this.locale);
404
+ else if (__classPrivateFieldGet(this, _TAFParser_validityPattern, "f").test(line[i]))
405
+ trend.validity = parseValidity(line[i]);
406
+ else
407
+ super.generalParse(trend, line[i]);
408
+ }
409
+ }
410
+ makeEmptyTAFTrend(type) {
411
+ return {
412
+ type,
413
+ remarks: [],
414
+ clouds: [],
415
+ weatherConditions: [],
416
+ };
417
+ }
418
+ }
419
+ exports.TAFParser = TAFParser;
420
+ _TAFParser_validityPattern = new WeakMap();
297
421
  class RemarkParser {
298
422
  constructor(locale) {
299
423
  this.locale = locale;
package/package.json CHANGED
@@ -1,7 +1,16 @@
1
1
  {
2
2
  "name": "metar-taf-parser",
3
- "version": "0.0.2",
4
- "description": "Parse METAR and TAF files",
3
+ "version": "1.0.2",
4
+ "description": "Parse METAR and TAF reports",
5
+ "homepage": "https://aeharding.github.io/metar-taf-parser",
6
+ "keywords": [
7
+ "aviation",
8
+ "weather",
9
+ "metar",
10
+ "taf",
11
+ "report",
12
+ "deserialize"
13
+ ],
5
14
  "main": "./dist/index.js",
6
15
  "types": "./dist/index.d.ts",
7
16
  "author": "Alexander Harding <2166114+aeharding@users.noreply.github.com>",
@@ -11,14 +20,14 @@
11
20
  "url": "https://github.com/aeharding/metar-taf-parser.git"
12
21
  },
13
22
  "scripts": {
14
- "build": "yarn clean; tsc && tsc-alias",
23
+ "build": "yarn clean; tsc --project tsconfig.build.json && tsc-alias",
15
24
  "clean": "rm -rf ./dist",
16
25
  "start": "node --experimental-specifier-resolution=node --loader ts-node/esm lib",
17
26
  "check-types": "tsc --noEmit",
18
27
  "check-formatting": "prettier --check '**/*.{js,json,css,md,scss,tsx,ts}'",
19
- "watch": "watch 'yarn build' lib",
20
- "test": "jest",
21
- "test-watch": "jest --watch",
28
+ "watch": "watch 'yarn build' src",
29
+ "test": "jest --coverage",
30
+ "test-watch": "jest --watch --coverage",
22
31
  "prepublishOnly": "yarn build"
23
32
  },
24
33
  "files": [