soaring-symbols 0.2.1 โ†’ 0.2.3

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 (118) hide show
  1. package/API.md +30 -19
  2. package/README.md +19 -12
  3. package/dist/airlines.json +1 -1
  4. package/dist/assets/air-arabia/icon-mono.svg +1 -1
  5. package/dist/assets/air-arabia/icon.svg +1 -1
  6. package/dist/assets/air-berlin/icon-mono.svg +1 -1
  7. package/dist/assets/air-berlin/icon.svg +1 -1
  8. package/dist/assets/air-berlin/logo-mono.svg +1 -1
  9. package/dist/assets/air-berlin/logo.svg +1 -1
  10. package/dist/assets/airasia-cambodia/logo-mono.svg +1 -0
  11. package/dist/assets/airasia-cambodia/logo.svg +1 -0
  12. package/dist/assets/american-airlines/icon.svg +1 -1
  13. package/dist/assets/american-airlines/logo.svg +1 -1
  14. package/dist/assets/austrian-airlines/logo.svg +1 -1
  15. package/dist/assets/avianca-costa-rica/icon-mono.svg +1 -0
  16. package/dist/assets/avianca-costa-rica/icon.svg +1 -0
  17. package/dist/assets/avianca-costa-rica/logo-mono.svg +1 -0
  18. package/dist/assets/avianca-costa-rica/logo.svg +1 -0
  19. package/dist/assets/avianca-ecuador/icon-mono.svg +1 -0
  20. package/dist/assets/avianca-ecuador/icon.svg +1 -0
  21. package/dist/assets/avianca-ecuador/logo-mono.svg +1 -0
  22. package/dist/assets/avianca-ecuador/logo.svg +1 -0
  23. package/dist/assets/avianca-el-salvador/icon-mono.svg +1 -0
  24. package/dist/assets/avianca-el-salvador/icon.svg +1 -0
  25. package/dist/assets/avianca-el-salvador/logo-mono.svg +1 -0
  26. package/dist/assets/avianca-el-salvador/logo.svg +1 -0
  27. package/dist/assets/azul/icon.svg +1 -1
  28. package/dist/assets/binter/logo-mono.svg +1 -0
  29. package/dist/assets/binter/logo.svg +1 -0
  30. package/dist/assets/cebu-pacific/icon.svg +1 -1
  31. package/dist/assets/cebu-pacific/logo.svg +1 -1
  32. package/dist/assets/china-airlines/logo.svg +1 -1
  33. package/dist/assets/china-eastern-airlines/icon.svg +1 -1
  34. package/dist/assets/china-southern-airlines/tail.svg +1 -1
  35. package/dist/assets/condor/icon-mono.svg +1 -1
  36. package/dist/assets/condor/icon.svg +1 -1
  37. package/dist/assets/condor/logo-mono.svg +1 -1
  38. package/dist/assets/condor/logo.svg +1 -1
  39. package/dist/assets/delta-air-lines/icon-mono.svg +1 -1
  40. package/dist/assets/easyjet/logo-mono.svg +1 -1
  41. package/dist/assets/easyjet/logo.svg +1 -1
  42. package/dist/assets/egyptair/icon-mono.svg +1 -1
  43. package/dist/assets/egyptair/icon.svg +1 -1
  44. package/dist/assets/flybe/logo-mono.svg +1 -1
  45. package/dist/assets/flybe/logo.svg +1 -1
  46. package/dist/assets/frontier-airlines/icon-mono.svg +1 -1
  47. package/dist/assets/frontier-airlines/icon.svg +1 -1
  48. package/dist/assets/gol/logo.svg +1 -1
  49. package/dist/assets/hawaiian-airlines/icon.svg +1 -1
  50. package/dist/assets/hawaiian-airlines/logo.svg +1 -1
  51. package/dist/assets/indonesia-airasia/logo-mono.svg +1 -0
  52. package/dist/assets/indonesia-airasia/logo.svg +1 -0
  53. package/dist/assets/ita-airways/logo.svg +1 -1
  54. package/dist/assets/jet2com/icon-mono.svg +1 -1
  55. package/dist/assets/jet2com/icon.svg +1 -1
  56. package/dist/assets/jetstar-japan/icon-mono.svg +1 -0
  57. package/dist/assets/jetstar-japan/icon.svg +1 -0
  58. package/dist/assets/jetstar-japan/logo-mono.svg +1 -0
  59. package/dist/assets/jetstar-japan/logo.svg +1 -0
  60. package/dist/assets/juneyao-air/icon-mono.svg +1 -0
  61. package/dist/assets/juneyao-air/icon.svg +1 -0
  62. package/dist/assets/juneyao-air/logo-mono.svg +1 -0
  63. package/dist/assets/juneyao-air/logo.svg +1 -0
  64. package/dist/assets/latam-airlines-brasil/icon-mono.svg +1 -0
  65. package/dist/assets/latam-airlines-brasil/icon.svg +1 -0
  66. package/dist/assets/latam-airlines-brasil/logo-mono.svg +1 -0
  67. package/dist/assets/latam-airlines-brasil/logo.svg +1 -0
  68. package/dist/assets/latam-airlines-colombia/icon-mono.svg +1 -0
  69. package/dist/assets/latam-airlines-colombia/icon.svg +1 -0
  70. package/dist/assets/latam-airlines-colombia/logo-mono.svg +1 -0
  71. package/dist/assets/latam-airlines-colombia/logo.svg +1 -0
  72. package/dist/assets/latam-airlines-ecuador/icon-mono.svg +1 -0
  73. package/dist/assets/latam-airlines-ecuador/icon.svg +1 -0
  74. package/dist/assets/latam-airlines-ecuador/logo-mono.svg +1 -0
  75. package/dist/assets/latam-airlines-ecuador/logo.svg +1 -0
  76. package/dist/assets/latam-airlines-paraguay/icon-mono.svg +1 -0
  77. package/dist/assets/latam-airlines-paraguay/icon.svg +1 -0
  78. package/dist/assets/latam-airlines-paraguay/logo-mono.svg +1 -0
  79. package/dist/assets/latam-airlines-paraguay/logo.svg +1 -0
  80. package/dist/assets/latam-airlines-peru/icon-mono.svg +1 -0
  81. package/dist/assets/latam-airlines-peru/icon.svg +1 -0
  82. package/dist/assets/latam-airlines-peru/logo-mono.svg +1 -0
  83. package/dist/assets/latam-airlines-peru/logo.svg +1 -0
  84. package/dist/assets/level/icon.svg +1 -0
  85. package/dist/assets/level/logo.svg +1 -0
  86. package/dist/assets/lion-air/icon-mono.svg +1 -1
  87. package/dist/assets/lion-air/icon.svg +1 -1
  88. package/dist/assets/norwegian-air-shuttle/icon.svg +1 -1
  89. package/dist/assets/norwegian-air-shuttle/logo-mono.svg +1 -1
  90. package/dist/assets/norwegian-air-shuttle/logo.svg +1 -1
  91. package/dist/assets/pegasus-airlines/icon-mono.svg +1 -1
  92. package/dist/assets/pegasus-airlines/icon.svg +1 -1
  93. package/dist/assets/philippines-airasia/logo-mono.svg +1 -0
  94. package/dist/assets/philippines-airasia/logo.svg +1 -0
  95. package/dist/assets/sas-connect/logo-mono.svg +1 -0
  96. package/dist/assets/sas-connect/logo.svg +1 -0
  97. package/dist/assets/spirit-airlines/logo-mono.svg +1 -1
  98. package/dist/assets/spirit-airlines/logo.svg +1 -1
  99. package/dist/assets/thai-airasia/logo-mono.svg +1 -0
  100. package/dist/assets/thai-airasia/logo.svg +1 -0
  101. package/dist/assets/viva/icon-mono.svg +1 -1
  102. package/dist/assets/viva/icon.svg +1 -1
  103. package/dist/assets/volaris/icon.svg +1 -1
  104. package/dist/assets/vueling/logo.svg +1 -1
  105. package/dist/assets/winair/logo-mono.svg +1 -0
  106. package/dist/assets/winair/logo.svg +1 -0
  107. package/dist/assets/wizz-air-abu-dhabi/logo-mono.svg +1 -0
  108. package/dist/assets/wizz-air-abu-dhabi/logo.svg +1 -0
  109. package/dist/assets/wizz-air-uk/logo-mono.svg +1 -0
  110. package/dist/assets/wizz-air-uk/logo.svg +1 -0
  111. package/dist/index.d.cts +7 -11
  112. package/dist/index.d.ts +7 -11
  113. package/dist/svg-validator.cjs +382 -5
  114. package/dist/svg-validator.d.cts +12 -0
  115. package/dist/svg-validator.d.ts +12 -0
  116. package/dist/svg-validator.js +382 -5
  117. package/package.json +4 -3
  118. package/AIRLINES.md +0 -144
@@ -4,6 +4,17 @@
4
4
 
5
5
  const r = (n) => Math.round(n * 1000) / 1000
6
6
 
7
+ // Top-level constants are inlined by the build like the functions; only
8
+ // functions may be exported (see scripts/build.js)
9
+ const HEX = /^#(?:[0-9A-F]{3}){1,2}$/i
10
+ // Paint keywords that are not colors to check or move
11
+ const PAINT_KEYWORDS = /^(none|currentColor|transparent|inherit)$/i
12
+
13
+ // Whether a file is a monochrome variant, drawn in currentColor
14
+ function isMonoFile(name) {
15
+ return name.toLowerCase().endsWith('-mono.svg')
16
+ }
17
+
7
18
  // Check: <title> exists and has non-empty text content
8
19
  function validateTitle(svg) {
9
20
  const title = svg.querySelector('title')
@@ -83,6 +94,17 @@ function validateNoTransforms(svg) {
83
94
  return { pass, detail }
84
95
  }
85
96
 
97
+ // Check: no <g> inside another <g> (a single level is fine for shared attributes)
98
+ function validateNoNestedGroups(svg) {
99
+ const nested = svg.querySelectorAll('g g').length
100
+ const pass = nested === 0
101
+
102
+ return {
103
+ pass,
104
+ detail: pass ? 'clean' : `${nested} nested <g> found`,
105
+ }
106
+ }
107
+
86
108
  // Check: geometry โ€” fills viewBox, fits within it, and is centered.
87
109
  // Requires a pre-computed bbox (e.g. from getBBox() in the browser).
88
110
  // Returns an array of SvgValidation-shaped objects (may be 1โ€“3 items).
@@ -151,12 +173,367 @@ function validateGeometry(type, bbox) {
151
173
  return results
152
174
  }
153
175
 
176
+ // Check: <title> is the first element inside <svg>, so screen readers and
177
+ // the catalog read it before anything else
178
+ function validateTitleFirst(svg) {
179
+ const title = svg.querySelector('title')
180
+ const first = svg.firstElementChild
181
+ const pass = !!title && first === title
182
+
183
+ return {
184
+ pass,
185
+ detail: pass
186
+ ? 'first child'
187
+ : title
188
+ ? `<${first.tagName.toLowerCase()}> comes before <title>`
189
+ : 'missing',
190
+ }
191
+ }
192
+
193
+ // Check: no width or height on the root <svg>; the viewBox sets the size, so
194
+ // the marks scale to whatever box they are placed in
195
+ function validateNoDimensions(svg) {
196
+ const attrs = ['width', 'height'].filter((a) => svg.hasAttribute(a))
197
+
198
+ return {
199
+ pass: attrs.length === 0,
200
+ detail: attrs.length
201
+ ? attrs.map((a) => `${a}="${svg.getAttribute(a)}"`).join(' ')
202
+ : 'clean',
203
+ attrs,
204
+ }
205
+ }
206
+
207
+ // Check: no comments, <metadata> or design-tool namespaces and attributes
208
+ // (Inkscape, Sketch, Illustratorโ€ฆ) inside <svg>
209
+ function validateNoEditorData(svg) {
210
+ const editorPrefix = /^(inkscape|sodipodi|sketch|serif|i|x|graph|a):/
211
+ const editorNs = /^xmlns:(?!xlink$)/
212
+ const walker = svg.ownerDocument.createTreeWalker(svg, 128) // comments
213
+ const comments = []
214
+ while (walker.nextNode()) comments.push(walker.currentNode)
215
+
216
+ const leftovers = new Set()
217
+ for (const el of [svg, ...svg.querySelectorAll('*')]) {
218
+ if (
219
+ el.tagName.toLowerCase() === 'metadata' ||
220
+ editorPrefix.test(el.tagName)
221
+ ) {
222
+ leftovers.add(`<${el.tagName}>`)
223
+ }
224
+ for (const { name } of el.attributes) {
225
+ if (editorPrefix.test(name) || editorNs.test(name)) {
226
+ leftovers.add(name)
227
+ }
228
+ }
229
+ }
230
+ const list = [...leftovers]
231
+ const found = comments.length ? ['comments', ...list] : list
232
+
233
+ return {
234
+ pass: found.length === 0,
235
+ detail: found.length ? found.join(', ') : 'clean',
236
+ comments,
237
+ leftovers: list,
238
+ }
239
+ }
240
+
241
+ // Check: path data is written the way SVGO writes it: at most five decimals,
242
+ // no negative zero and no leading zero (".5", not "0.5"). `paths` lists what
243
+ // each <path> breaks, with `trimmed` the d without leading zeros.
244
+ function validateNumberFormat(svg) {
245
+ const maxDecimals = 5
246
+ const paths = []
247
+ for (const el of svg.querySelectorAll('path[d]')) {
248
+ const d = el.getAttribute('d')
249
+ const long = d.match(new RegExp(`\\.\\d{${maxDecimals + 1},}`))
250
+ const negativeZero = /-0(?![\d.])/.test(d)
251
+ // "0.5" -> ".5" and "-0.5" -> "-.5": the same numbers, written shorter
252
+ const trimmed = d.replace(/(?<![\d.])0\.(?=\d)/g, '.')
253
+ if (long || negativeZero || trimmed !== d) {
254
+ paths.push({
255
+ el,
256
+ long: long?.[0] ?? null,
257
+ negativeZero,
258
+ trimmed: trimmed !== d ? trimmed : null,
259
+ })
260
+ }
261
+ }
262
+ const problems = [
263
+ paths.some((p) => p.long) &&
264
+ `more than ${maxDecimals} decimals ("${paths.find((p) => p.long).long}")`,
265
+ paths.some((p) => p.negativeZero) && 'negative zero',
266
+ paths.some((p) => p.trimmed) && 'leading zeros ("0.5" โ†’ ".5")',
267
+ ].filter(Boolean)
268
+
269
+ return {
270
+ pass: paths.length === 0,
271
+ detail: problems.length ? problems.join(' ยท ') : 'clean',
272
+ maxDecimals,
273
+ paths,
274
+ }
275
+ }
276
+
277
+ // The six-digit lowercase form of a hex color: #F60 -> #ff6600
278
+ function toFullHex(hex) {
279
+ return (
280
+ hex.length === 4
281
+ ? '#' + [...hex.slice(1)].map((c) => c + c).join('')
282
+ : hex
283
+ ).toLowerCase()
284
+ }
285
+
286
+ // Check: colors in fill, stroke and stop-color are six-digit lowercase hex.
287
+ // `invalid` holds values that aren't hex at all (color names, rgb()), `short`
288
+ // hex that only needs rewriting.
289
+ function validateHexColors(svg) {
290
+ const invalid = []
291
+ const short = []
292
+ for (const attr of ['fill', 'stroke', 'stop-color']) {
293
+ const els = [svg, ...svg.querySelectorAll(`[${attr}]`)]
294
+ for (const el of els.filter((e) => e.hasAttribute(attr))) {
295
+ const value = el.getAttribute(attr).trim()
296
+ if (PAINT_KEYWORDS.test(value) || /^url\(.*\)$/.test(value))
297
+ continue
298
+ if (!HEX.test(value)) {
299
+ invalid.push({ el, attr, value })
300
+ continue
301
+ }
302
+ const full = toFullHex(value)
303
+ if (value !== full) short.push({ el, attr, value, full })
304
+ }
305
+ }
306
+ const problems = [
307
+ ...invalid.map((c) => `${c.attr}="${c.value}" is not hex`),
308
+ ...short.map((c) => `"${c.value}" should be "${c.full}"`),
309
+ ]
310
+
311
+ return {
312
+ pass: problems.length === 0,
313
+ detail: problems.length ? problems.join(' ยท ') : 'clean',
314
+ invalid,
315
+ short,
316
+ }
317
+ }
318
+
319
+ // Check: a single-color SVG sets its fill once, on the root <svg>
320
+ // (currentColor for a -mono file), and none of its children has one; a
321
+ // multi-color SVG has no fill on the root. Gradients and non-hex fills are
322
+ // left alone (`skipped`): validateHexColors reports the latter.
323
+ // `rootFill` is the fill the root should have (null for none), `rootOk`
324
+ // whether it has it, and `redundant` the children whose fill should go.
325
+ function validateFillPlacement(svg, isMono = false) {
326
+ // The root counts: a single-color SVG keeps its fill there
327
+ const withFill = [svg, ...svg.querySelectorAll('[fill]')].filter((el) =>
328
+ el.hasAttribute('fill'),
329
+ )
330
+ const fills = withFill.map((el) => el.getAttribute('fill').trim())
331
+ const result = (pass, detail, rest) => ({
332
+ pass,
333
+ detail,
334
+ skipped: false,
335
+ rootFill: null,
336
+ rootOk: true,
337
+ redundant: [],
338
+ ...rest,
339
+ })
340
+
341
+ if (fills.some((f) => !HEX.test(f) && !PAINT_KEYWORDS.test(f))) {
342
+ return result(true, 'skipped: gradient or non-hex fill', {
343
+ skipped: true,
344
+ })
345
+ }
346
+
347
+ const colors = new Set(
348
+ fills.filter((f) => HEX.test(f)).map((f) => f.toLowerCase()),
349
+ )
350
+ const existing = svg.getAttribute('fill')
351
+
352
+ if (colors.size > 1) {
353
+ const rootOk = existing === null
354
+
355
+ return result(
356
+ rootOk,
357
+ rootOk
358
+ ? `${colors.size} colors, no root fill`
359
+ : 'root <svg> should have no fill in a multi-color SVG',
360
+ { rootOk },
361
+ )
362
+ }
363
+
364
+ const rootFill = isMono ? 'currentColor' : [...colors][0] || 'currentColor'
365
+ const rootOk = isMono
366
+ ? existing === rootFill
367
+ : existing?.toLowerCase() === rootFill
368
+ const redundant = withFill.filter((el) => el !== svg)
369
+ const problems = [
370
+ !rootOk && `root <svg> should have fill="${rootFill}"`,
371
+ redundant.length &&
372
+ `remove fill from ${redundant.length} child element${redundant.length > 1 ? 's' : ''}`,
373
+ ].filter(Boolean)
374
+
375
+ return result(
376
+ problems.length === 0,
377
+ problems.length ? problems.join(' ยท ') : `fill="${rootFill}"`,
378
+ { rootFill, rootOk, redundant },
379
+ )
380
+ }
381
+
382
+ // Check (warning): a single-color SVG draws everything in one <path>
383
+ function validateSinglePath(svg, isMono = false) {
384
+ const hexFill = /#[0-9a-f]{3}(?:[0-9a-f]{3})?\b/gi
385
+ const markup = svg.outerHTML
386
+ const usesPaint = /url\(#|stroke=|stroke:|<(clipPath|mask)\b/i.test(markup)
387
+ const fills = new Set(
388
+ [...svg.querySelectorAll('[fill], [style]')].flatMap((el) =>
389
+ (
390
+ `${el.getAttribute('fill') || ''} ${el.getAttribute('style') || ''}`.match(
391
+ hexFill,
392
+ ) || []
393
+ ).map((c) => c.toLowerCase()),
394
+ ),
395
+ )
396
+ if (!isMono && (usesPaint || fills.size > 1)) {
397
+ return { pass: true, detail: 'multi-color', count: 0 }
398
+ }
399
+
400
+ const count = [
401
+ ...svg.querySelectorAll(
402
+ 'path, circle, rect, ellipse, polygon, polyline, line',
403
+ ),
404
+ ].filter((el) => !el.closest('defs, clipPath, mask')).length
405
+
406
+ return {
407
+ pass: count <= 1,
408
+ detail: count <= 1 ? 'one shape' : `${count} shapes`,
409
+ count,
410
+ }
411
+ }
412
+
413
+ // The file with anything before <svg> (XML declaration, DOCTYPE, comment)
414
+ // removed
415
+ function stripProlog(source) {
416
+ const start = source.indexOf('<svg')
417
+
418
+ return start > 0 ? source.slice(start) : source
419
+ }
420
+
421
+ // The file with fill before d on every <path>
422
+ function orderPathAttrs(source) {
423
+ return source.replace(
424
+ /<path([^>]*?) (d="[^"]*?")([^>]*?) (fill="[^"]*?")/g,
425
+ (match, before, dAttr, after, fillAttr) =>
426
+ `<path${before} ${fillAttr} ${dAttr}${after}`,
427
+ )
428
+ }
429
+
430
+ // The file with the root <svg> attributes ordered role, viewBox, xmlns, fill,
431
+ // then the rest
432
+ function orderSvgAttrs(source) {
433
+ const first = ['role', 'viewBox', 'xmlns', 'fill']
434
+ const svgTag = source.match(/<svg([^>]*)>/)
435
+ if (!svgTag) return source
436
+
437
+ const attributes = {}
438
+ const attrRegex = /([a-zA-Z0-9:-]+)="([^"]*)"/g
439
+ let match
440
+ while ((match = attrRegex.exec(svgTag[1])) !== null) {
441
+ attributes[match[1]] = match[2]
442
+ }
443
+ const ordered = [
444
+ ...first.filter((k) => k in attributes),
445
+ ...Object.keys(attributes).filter((k) => !first.includes(k)),
446
+ ]
447
+ .map((k) => `${k}="${attributes[k]}"`)
448
+ .join(' ')
449
+ if (ordered.replace(/\s+/g, '') === svgTag[1].trim().replace(/\s+/g, '')) {
450
+ return source
451
+ }
452
+
453
+ return source.replace(svgTag[0], `<svg ${ordered}>`)
454
+ }
455
+
456
+ // Check: the file starts with <svg>, with nothing before it
457
+ function validateNoProlog(source) {
458
+ const pass = stripProlog(source) === source
459
+
460
+ return {
461
+ pass,
462
+ detail: pass ? 'clean' : 'content before <svg>',
463
+ }
464
+ }
465
+
466
+ // Check: root <svg> attributes come as role, viewBox, xmlns, fill, then the
467
+ // rest, and fill comes before d on <path>
468
+ function validateAttrOrder(source) {
469
+ const svgOk = orderSvgAttrs(source) === source
470
+ const pathOk = orderPathAttrs(source) === source
471
+ const problems = [
472
+ !svgOk && 'root <svg>: role, viewBox, xmlns, fill first',
473
+ !pathOk && '<path>: fill before d',
474
+ ].filter(Boolean)
475
+
476
+ return {
477
+ pass: problems.length === 0,
478
+ detail: problems.length ? problems.join(' ยท ') : 'clean',
479
+ }
480
+ }
481
+
482
+
483
+ // The same checks as `scripts/lint-svg.js`, in the browser. `fileName` tells
484
+ // a -mono.svg apart (its fill is currentColor); `source`, the file's text,
485
+ // adds the check that needs it (nothing before <svg>) and is what the
486
+ // attribute order is read from, falling back to the parsed element as the
487
+ // CLI does.
488
+ export function runSvgValidations(svg, type, bbox = null, options = {}) {
489
+ const { fileName = '', source = null } = options
490
+ const isMono = isMonoFile(fileName)
491
+ const row = (group, label, result, level = 'error') => ({
492
+ group,
493
+ label,
494
+ level,
495
+ pass: result.pass,
496
+ detail: result.detail,
497
+ })
154
498
 
155
- export function runSvgValidations(svg, type, bbox = null) {
156
499
  return [
157
- { label: 'Required attributes', ...validateRequiredAttrs(svg, type) },
158
- { label: 'No embedded scripts', ...validateNoScripts(svg) },
159
- { label: 'No transforms', ...validateNoTransforms(svg) },
160
- ...validateGeometry(type, bbox),
500
+ row(
501
+ 'Structure',
502
+ 'Required attributes',
503
+ validateRequiredAttrs(svg, type),
504
+ ),
505
+ row('Structure', 'Title first', validateTitleFirst(svg)),
506
+ row('Structure', 'No embedded scripts', validateNoScripts(svg)),
507
+ row('Structure', 'No transforms', validateNoTransforms(svg)),
508
+ row(
509
+ 'Structure',
510
+ 'No nested groups',
511
+ validateNoNestedGroups(svg),
512
+ 'warning',
513
+ ),
514
+ row(
515
+ 'Structure',
516
+ 'Single path',
517
+ validateSinglePath(svg, isMono),
518
+ 'warning',
519
+ ),
520
+ row('Format', 'No width or height', validateNoDimensions(svg)),
521
+ row('Format', 'No editor data', validateNoEditorData(svg)),
522
+ row('Format', 'Number format', validateNumberFormat(svg)),
523
+ ...(source === null
524
+ ? []
525
+ : [row('Format', 'Starts with <svg>', validateNoProlog(source))]),
526
+ row(
527
+ 'Format',
528
+ 'Attribute order',
529
+ validateAttrOrder(source ?? svg.outerHTML),
530
+ ),
531
+ row('Style', 'Hex colors', validateHexColors(svg)),
532
+ row('Style', 'Fill placement', validateFillPlacement(svg, isMono)),
533
+ ...validateGeometry(type, bbox).map((check) => ({
534
+ group: 'Geometry',
535
+ level: 'error',
536
+ ...check,
537
+ })),
161
538
  ]
162
539
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "soaring-symbols",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "A curated collection of airline branding assets โ€” logos, icons, and tail designs in SVG format.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -59,7 +59,6 @@
59
59
  ],
60
60
  "files": [
61
61
  "dist/",
62
- "AIRLINES.md",
63
62
  "API.md",
64
63
  "LICENSE",
65
64
  "README.md"
@@ -76,9 +75,11 @@
76
75
  "lint": "eslint .",
77
76
  "lint:fix": "eslint . --fix",
78
77
  "format": "prettier --write \"**/*.{js,ts}\"",
78
+ "format:check": "prettier --check \"**/*.{js,ts}\"",
79
79
  "release": "node scripts/release.js",
80
80
  "sync:data": "node scripts/sync-data.js",
81
- "optimize": "svgo --config svgo.config.mjs"
81
+ "optimize": "svgo --config svgo.config.mjs",
82
+ "optimize:check": "node scripts/check-optimized.js"
82
83
  },
83
84
  "lint-staged": {
84
85
  "*.js": [
package/AIRLINES.md DELETED
@@ -1,144 +0,0 @@
1
- # Soaring Symbols: Airlines
2
-
3
- This file provides an overview of the airlines included in the Soaring Symbols project.
4
-
5
- | Airline | Country | IATA | ICAO | Alliance | Primary Color | Icon | Logo | Tail |
6
- |:---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
7
- | [Aegean Airlines](https://en.aegeanair.com) ๐Ÿ‡ฌ๐Ÿ‡ท | GR | A3 | AEE | Star Alliance | ![#002677](https://place-hold.it/10x10/002677/002677.png) | โœ… | โœ… | |
8
- | [Aer Lingus](https://www.aerlingus.com) ๐Ÿ‡ฎ๐Ÿ‡ช | IE | EI | EIN | | ![#006472](https://place-hold.it/10x10/006472/006472.png) | โ˜‘๏ธ | โ˜‘๏ธ | |
9
- | [Aerolรญneas Argentinas](https://www.aerolineas.com.ar) ๐Ÿ‡ฆ๐Ÿ‡ท | AR | AR | ARG | SkyTeam | ![#0080be](https://place-hold.it/10x10/0080be/0080be.png) | โœ… | โœ… | |
10
- | [Aeromรฉxico](https://www.aeromexico.com) ๐Ÿ‡ฒ๐Ÿ‡ฝ | MX | AM | AMX | SkyTeam | ![#020c41](https://place-hold.it/10x10/020c41/020c41.png) | โœ… | โœ… | |
11
- | [Air Albania](https://www.airalbania.com.al) ๐Ÿ‡ฆ๐Ÿ‡ฑ | AL | ZB | ABN | | ![#ce1f2e](https://place-hold.it/10x10/ce1f2e/ce1f2e.png) | โœ… | โœ… | |
12
- | [Air Algรฉrie](https://airalgerie.dz) ๐Ÿ‡ฉ๐Ÿ‡ฟ | DZ | AH | DAH | | ![#e2231a](https://place-hold.it/10x10/e2231a/e2231a.png) | โœ… | โœ… | |
13
- | [Air Arabia](https://www.airarabia.com) | AE | G9 | ABY | | ![#ed1b48](https://place-hold.it/10x10/ed1b48/ed1b48.png) | โœ… | โœ… | |
14
- | [Air Astana](https://airastana.com) ๐Ÿ‡ฐ๐Ÿ‡ฟ | KZ | KC | KZR | | ![#a48c5d](https://place-hold.it/10x10/a48c5d/a48c5d.png) | โœ… | โœ… | |
15
- | [Air Berlin](https://www.airberlin.com) | DE | AB | BER | oneworld | ![#cc2234](https://place-hold.it/10x10/cc2234/cc2234.png) | โœ… | โœ… | |
16
- | [Air Canada](https://www.aircanada.com) ๐Ÿ‡จ๐Ÿ‡ฆ | CA | AC | ACA | Star Alliance | ![#f01428](https://place-hold.it/10x10/f01428/f01428.png) | โœ… | โœ… | |
17
- | [Air China](https://www.airchina.com) ๐Ÿ‡จ๐Ÿ‡ณ | CN | CA | CCA | Star Alliance | ![#ed1c24](https://place-hold.it/10x10/ed1c24/ed1c24.png) | โœ… | โœ… | |
18
- | [Air Dolomiti](https://www.airdolomiti.eu) | IT | EN | DLA | | ![#009eb3](https://place-hold.it/10x10/009eb3/009eb3.png) | โœ… | โœ… | |
19
- | [Air Europa](https://www.aireuropa.com) | ES | UX | AEA | SkyTeam | ![#006fde](https://place-hold.it/10x10/006fde/006fde.png) | โœ… | โœ… | |
20
- | [Air France](https://airfrance.com) ๐Ÿ‡ซ๐Ÿ‡ท | FR | AF | AFR | SkyTeam | ![#051039](https://place-hold.it/10x10/051039/051039.png) | โœ… | โœ… | |
21
- | [Air India](https://www.airindia.com) ๐Ÿ‡ฎ๐Ÿ‡ณ | IN | AI | AIC | Star Alliance | ![#da0e29](https://place-hold.it/10x10/da0e29/da0e29.png) | โœ… | โœ… | |
22
- | [Air Mauritius](https://www.airmauritius.com) ๐Ÿ‡ฒ๐Ÿ‡บ | MU | MK | MAU | Vanilla Alliance | ![#be2239](https://place-hold.it/10x10/be2239/be2239.png) | โœ… | โœ… | |
23
- | [Air New Zealand](https://www.airnewzealand.co.nz) ๐Ÿ‡ณ๐Ÿ‡ฟ | NZ | NZ | ANZ | Star Alliance | ![#000000](https://place-hold.it/10x10/000000/000000.png) | โœ… | โœ… | |
24
- | [Air Serbia](https://www.airserbia.com) ๐Ÿ‡ท๐Ÿ‡ธ | RS | JU | ASL | | ![#0e203f](https://place-hold.it/10x10/0e203f/0e203f.png) | โœ… | โœ… | |
25
- | [Air Transat](https://airtransat.com) | CA | TS | TSC | | ![#172b54](https://place-hold.it/10x10/172b54/172b54.png) | โœ… | โœ… | |
26
- | [AirAsia](https://www.airasia.com) | MY | AK | AXM | | ![#ff0000](https://place-hold.it/10x10/ff0000/ff0000.png) | | โœ… | |
27
- | AirAsia Cambodia | KH | KT | KTC | | ![#ff0000](https://place-hold.it/10x10/ff0000/ff0000.png) | | โœ… | |
28
- | Thai AirAsia | TH | FD | AIQ | | ![#ff0000](https://place-hold.it/10x10/ff0000/ff0000.png) | | โœ… | |
29
- | Indonesia AirAsia | ID | QZ | AWQ | | ![#ff0000](https://place-hold.it/10x10/ff0000/ff0000.png) | | โœ… | |
30
- | Philippines AirAsia | PH | Z2 | APG | | ![#ff0000](https://place-hold.it/10x10/ff0000/ff0000.png) | | โœ… | |
31
- | [airBaltic](https://www.airbaltic.com) ๐Ÿ‡ฑ๐Ÿ‡ป | LV | BT | BTI | | ![#152649](https://place-hold.it/10x10/152649/152649.png) | | โœ… | |
32
- | [Akasa Air](https://www.akasaair.com) | IN | QP | AKJ | | ![#ff6300](https://place-hold.it/10x10/ff6300/ff6300.png) | โœ… | โœ… | |
33
- | [Alaska Airlines](https://www.alaskaair.com) | US | AS | ASA | oneworld | ![#01426a](https://place-hold.it/10x10/01426a/01426a.png) | | โœ… | |
34
- | [All Nippon Airways](https://www.ana.co.jp) | JP | NH | ANA | Star Alliance | ![#00146e](https://place-hold.it/10x10/00146e/00146e.png) | | โ˜‘๏ธ | |
35
- | [American Airlines](https://www.aa.com) | US | AA | AAL | oneworld | ![#0078d2](https://place-hold.it/10x10/0078d2/0078d2.png) | โœ… | โœ… | |
36
- | [Asiana Airlines](https://flyasiana.com) | KR | OZ | AAR | Star Alliance | ![#4a4f54](https://place-hold.it/10x10/4a4f54/4a4f54.png) | โœ… | โœ… | |
37
- | [Atlantic Airways](https://www.atlanticairways.com) ๐Ÿ‡ซ๐Ÿ‡ด | FO | RC | FLI | | ![#004c93](https://place-hold.it/10x10/004c93/004c93.png) | โœ… | โœ… | |
38
- | [Austrian Airlines](https://www.austrian.com) ๐Ÿ‡ฆ๐Ÿ‡น | AT | OS | AUA | Star Alliance | ![#d81824](https://place-hold.it/10x10/d81824/d81824.png) | โœ… | โ˜‘๏ธ | |
39
- | [avianca](https://www.avianca.com) ๐Ÿ‡จ๐Ÿ‡ด | CO | AV | AVA | Star Alliance | ![#ff0000](https://place-hold.it/10x10/ff0000/ff0000.png) | โœ… | โœ… | |
40
- | Avianca Costa Rica ๐Ÿ‡จ๐Ÿ‡ท | CR | LR | LRC | | ![#ff0000](https://place-hold.it/10x10/ff0000/ff0000.png) | โœ… | โœ… | |
41
- | Avianca Ecuador | EC | 2K | GLG | | ![#ff0000](https://place-hold.it/10x10/ff0000/ff0000.png) | โœ… | โœ… | |
42
- | Avianca El Salvador ๐Ÿ‡ธ๐Ÿ‡ป | SV | TA | TAI | | ![#ff0000](https://place-hold.it/10x10/ff0000/ff0000.png) | โœ… | โœ… | |
43
- | [Azerbaijan Airlines](https://azal.az) ๐Ÿ‡ฆ๐Ÿ‡ฟ | AZ | J2 | AHY | | ![#082e6a](https://place-hold.it/10x10/082e6a/082e6a.png) | โœ… | โœ… | |
44
- | [Azul](https://www.voeazul.com.br) | BR | AD | AZU | | ![#005baa](https://place-hold.it/10x10/005baa/005baa.png) | โ˜‘๏ธ | โ˜‘๏ธ | |
45
- | [Bamboo Airways](https://www.bambooairways.com) | VN | QH | BAV | | ![#64ae53](https://place-hold.it/10x10/64ae53/64ae53.png) | โ˜‘๏ธ | โ˜‘๏ธ | |
46
- | [Bangkok Airways](https://www.bangkokair.com) | TH | PG | BKP | | ![#000f8f](https://place-hold.it/10x10/000f8f/000f8f.png) | โœ… | โœ… | |
47
- | [British Airways](https://www.britishairways.com) ๐Ÿ‡ฌ๐Ÿ‡ง | GB | BA | BAW | oneworld | ![#2e5c99](https://place-hold.it/10x10/2e5c99/2e5c99.png) | โœ… | โœ… | |
48
- | [Brussels Airlines](https://www.brusselsairlines.com) ๐Ÿ‡ง๐Ÿ‡ช | BE | SN | BEL | Star Alliance | ![#051446](https://place-hold.it/10x10/051446/051446.png) | โœ… | โœ… | |
49
- | [Cathay Pacific](https://www.cathaypacific.com) ๐Ÿ‡ญ๐Ÿ‡ฐ | HK | CX | CPA | oneworld | ![#005d63](https://place-hold.it/10x10/005d63/005d63.png) | โœ… | โœ… | |
50
- | [Cebu Pacific](https://www.cebupacificair.com) | PH | 5J | CEB | | ![#0096d6](https://place-hold.it/10x10/0096d6/0096d6.png) | โœ… | โœ… | |
51
- | [China Airlines](https://www.china-airlines.com) ๐Ÿ‡น๐Ÿ‡ผ | TW | CI | CAL | SkyTeam | ![#2b3088](https://place-hold.it/10x10/2b3088/2b3088.png) | โ˜‘๏ธ | โ˜‘๏ธ | |
52
- | [China Eastern Airlines](https://www.ceair.com) | CN | MU | CES | SkyTeam | ![#1a4fa3](https://place-hold.it/10x10/1a4fa3/1a4fa3.png) | โ˜‘๏ธ | โ˜‘๏ธ | |
53
- | [China Southern Airlines](https://www.csair.com) | CN | CZ | CSN | | ![#0093d0](https://place-hold.it/10x10/0093d0/0093d0.png) | โ˜‘๏ธ | โœ… | โ˜‘๏ธ |
54
- | [Condor](https://www.condor.com) | DE | DE | CFG | | ![#000000](https://place-hold.it/10x10/000000/000000.png) | โœ… | โœ… | |
55
- | [Copa Airlines](https://www.copaair.com) ๐Ÿ‡ต๐Ÿ‡ฆ | PA | CM | CMP | Star Alliance | ![#0032a0](https://place-hold.it/10x10/0032a0/0032a0.png) | โœ… | โœ… | |
56
- | [Delta Air Lines](https://www.delta.com) | US | DL | DAL | SkyTeam | ![#e31837](https://place-hold.it/10x10/e31837/e31837.png) | โœ… | โœ… | |
57
- | [easyJet](https://www.easyjet.com) | GB | U2 | EZY | | ![#ff6600](https://place-hold.it/10x10/ff6600/ff6600.png) | โ˜‘๏ธ | โœ… | |
58
- | [EgyptAir](https://www.egyptair.com) ๐Ÿ‡ช๐Ÿ‡ฌ | EG | MS | MSR | Star Alliance | ![#002855](https://place-hold.it/10x10/002855/002855.png) | โœ… | โœ… | |
59
- | [Emirates](https://www.emirates.com) ๐Ÿ‡ฆ๐Ÿ‡ช | AE | EK | UAE | | ![#d71921](https://place-hold.it/10x10/d71921/d71921.png) | โœ… | โœ… | |
60
- | [Ethiopian Airlines](https://www.ethiopianairlines.com) ๐Ÿ‡ช๐Ÿ‡น | ET | ET | ETH | Star Alliance | ![#b71d20](https://place-hold.it/10x10/b71d20/b71d20.png) | โœ… | โœ… | |
61
- | [Etihad Airways](https://www.etihad.com) ๐Ÿ‡ฆ๐Ÿ‡ช | AE | EY | ETD | | ![#bd8b13](https://place-hold.it/10x10/bd8b13/bd8b13.png) | | โœ… | |
62
- | [Eurowings](https://www.eurowings.com) | DE | EW | EWG | | ![#7c2045](https://place-hold.it/10x10/7c2045/7c2045.png) | โœ… | โœ… | |
63
- | [EVA Air](https://www.evaair.com) | TW | BR | EVA | Star Alliance | ![#00a64f](https://place-hold.it/10x10/00a64f/00a64f.png) | โ˜‘๏ธ | โœ… | โ˜‘๏ธ |
64
- | [Ewa Air](https://ewa-air.com) | FR | ZD | EWR | | ![#e1251b](https://place-hold.it/10x10/e1251b/e1251b.png) | | โœ… | |
65
- | [Fiji Airways](https://www.fijiairways.com) ๐Ÿ‡ซ๐Ÿ‡ฏ | FJ | FJ | FJI | oneworld | ![#473729](https://place-hold.it/10x10/473729/473729.png) | โœ… | โœ… | |
66
- | [Finnair](https://www.finnair.com) ๐Ÿ‡ซ๐Ÿ‡ฎ | FI | AY | FIN | oneworld | ![#0c0243](https://place-hold.it/10x10/0c0243/0c0243.png) | โœ… | โœ… | |
67
- | [Firefly](https://www.fireflyz.com.my) | MY | FY | FFM | | ![#ef5713](https://place-hold.it/10x10/ef5713/ef5713.png) | โœ… | โœ… | |
68
- | [Flybe](https://www.flybe.com) | GB | BE | BEE | | ![#5a3f99](https://place-hold.it/10x10/5a3f99/5a3f99.png) | | โœ… | |
69
- | [flydubai](https://www.flydubai.com) | AE | FZ | FDB | | ![#ff8200](https://place-hold.it/10x10/ff8200/ff8200.png) | | โ˜‘๏ธ | |
70
- | [flynas](https://www.flynas.com) | SA | XY | KNE | | ![#00b7ac](https://place-hold.it/10x10/00b7ac/00b7ac.png) | โœ… | โœ… | |
71
- | [Frontier Airlines](https://www.flyfrontier.com) | US | F9 | FFT | | ![#008c44](https://place-hold.it/10x10/008c44/008c44.png) | โœ… | โœ… | |
72
- | [Garuda Indonesia](https://www.garuda-indonesia.com) ๐Ÿ‡ฎ๐Ÿ‡ฉ | ID | GA | GIA | SkyTeam | ![#002560](https://place-hold.it/10x10/002560/002560.png) | โœ… | โœ… | |
73
- | [GOL](https://www.voegol.com.br) | BR | G3 | GLO | | ![#f58220](https://place-hold.it/10x10/f58220/f58220.png) | โ˜‘๏ธ | โ˜‘๏ธ | |
74
- | [Hainan Airlines](https://www.hainanairlines.com) | CN | HU | CHH | | ![#d71920](https://place-hold.it/10x10/d71920/d71920.png) | โœ… | โœ… | |
75
- | [Hawaiian Airlines](https://www.hawaiianairlines.com) | US | HA | HAL | oneworld | ![#4e2582](https://place-hold.it/10x10/4e2582/4e2582.png) | โ˜‘๏ธ | โ˜‘๏ธ | |
76
- | [HK Express](https://www.hkexpress.com) | HK | UO | HKE | | ![#702b91](https://place-hold.it/10x10/702b91/702b91.png) | โœ… | โœ… | |
77
- | [Iberia](https://www.iberia.com) ๐Ÿ‡ช๐Ÿ‡ธ | ES | IB | IBE | oneworld | ![#dc1c2e](https://place-hold.it/10x10/dc1c2e/dc1c2e.png) | โœ… | โœ… | |
78
- | [Icelandair](https://www.icelandair.com) ๐Ÿ‡ฎ๐Ÿ‡ธ | IS | FI | ICE | | ![#0c0243](https://place-hold.it/10x10/0c0243/0c0243.png) | โœ… | โœ… | โœ… |
79
- | [IndiGo](https://www.goindigo.in) | IN | 6E | IGO | | ![#001b94](https://place-hold.it/10x10/001b94/001b94.png) | โœ… | โœ… | |
80
- | [ITA Airways](https://www.ita-airways.com) ๐Ÿ‡ฎ๐Ÿ‡น | IT | AZ | ITY | Star Alliance | ![#006643](https://place-hold.it/10x10/006643/006643.png) | | โ˜‘๏ธ | |
81
- | [Japan Airlines](https://www.jal.co.jp) ๐Ÿ‡ฏ๐Ÿ‡ต | JP | JL | JAL | oneworld | ![#e50012](https://place-hold.it/10x10/e50012/e50012.png) | โœ… | โœ… | |
82
- | [Jet2.com](https://www.jet2.com) | GB | LS | EXS | | ![#d71920](https://place-hold.it/10x10/d71920/d71920.png) | โœ… | โœ… | |
83
- | [JetBlue Airways](https://www.jetblue.com) | US | B6 | JBU | | ![#003876](https://place-hold.it/10x10/003876/003876.png) | | โœ… | |
84
- | [Jetstar](https://www.jetstar.com) | AU | JQ | JST | | ![#ff5111](https://place-hold.it/10x10/ff5111/ff5111.png) | โœ… | โœ… | |
85
- | Jetstar Japan | JP | GK | JJP | | ![#ff5111](https://place-hold.it/10x10/ff5111/ff5111.png) | โœ… | โœ… | |
86
- | [Kenya Airways](https://www.kenya-airways.com) ๐Ÿ‡ฐ๐Ÿ‡ช | KE | KQ | KQA | SkyTeam | ![#ed1c24](https://place-hold.it/10x10/ed1c24/ed1c24.png) | โœ… | โœ… | |
87
- | [KLM](https://www.klm.com) ๐Ÿ‡ณ๐Ÿ‡ฑ | NL | KL | KLM | SkyTeam | ![#009fd9](https://place-hold.it/10x10/009fd9/009fd9.png) | โœ… | โœ… | |
88
- | [Korean Air](https://www.koreanair.com) ๐Ÿ‡ฐ๐Ÿ‡ท | KR | KE | KAL | | ![#051766](https://place-hold.it/10x10/051766/051766.png) | โœ… | โœ… | |
89
- | [Kuwait Airways](https://www.kuwaitairways.com) ๐Ÿ‡ฐ๐Ÿ‡ผ | KW | KU | KAC | | ![#1c285b](https://place-hold.it/10x10/1c285b/1c285b.png) | โœ… | โœ… | |
90
- | [LATAM Airlines](https://www.latamairlines.com) | CL | LA | LAN | | ![#1b0088](https://place-hold.it/10x10/1b0088/1b0088.png) | โœ… | โœ… | |
91
- | LATAM Airlines Brasil | BR | JJ | TAM | | ![#1b0088](https://place-hold.it/10x10/1b0088/1b0088.png) | โœ… | โœ… | |
92
- | LATAM Airlines Colombia | CO | 4C | ARE | | ![#1b0088](https://place-hold.it/10x10/1b0088/1b0088.png) | โœ… | โœ… | |
93
- | LATAM Airlines Ecuador | EC | XL | LNE | | ![#1b0088](https://place-hold.it/10x10/1b0088/1b0088.png) | โœ… | โœ… | |
94
- | LATAM Airlines Perรบ | PE | LP | LPE | | ![#1b0088](https://place-hold.it/10x10/1b0088/1b0088.png) | โœ… | โœ… | |
95
- | LATAM Airlines Paraguay | PY | PZ | LAP | | ![#1b0088](https://place-hold.it/10x10/1b0088/1b0088.png) | โœ… | โœ… | |
96
- | [Lion Air](https://www.lionair.co.id) | ID | JT | LNI | | ![#ed1b2f](https://place-hold.it/10x10/ed1b2f/ed1b2f.png) | โœ… | โœ… | |
97
- | [LOT Polish Airlines](https://www.lot.com) ๐Ÿ‡ต๐Ÿ‡ฑ | PL | LO | LOT | Star Alliance | ![#252668](https://place-hold.it/10x10/252668/252668.png) | โœ… | โœ… | |
98
- | [Lufthansa](https://www.lufthansa.com) ๐Ÿ‡ฉ๐Ÿ‡ช | DE | LH | DLH | Star Alliance | ![#05164d](https://place-hold.it/10x10/05164d/05164d.png) | โœ… | โœ… | |
99
- | [Malaysia Airlines](https://www.malaysiaairlines.com) ๐Ÿ‡ฒ๐Ÿ‡พ | MY | MH | MAS | oneworld | ![#174887](https://place-hold.it/10x10/174887/174887.png) | โœ… | โœ… | |
100
- | [Myanmar National Airlines](https://www.flymna.com) ๐Ÿ‡ฒ๐Ÿ‡ฒ | MM | UB | UBA | | ![#fdb515](https://place-hold.it/10x10/fdb515/fdb515.png) | โœ… | โœ… | |
101
- | [Norwegian Air Shuttle](https://www.norwegian.com) | NO | DY | NOZ | | ![#d81939](https://place-hold.it/10x10/d81939/d81939.png) | โ˜‘๏ธ | โœ… | |
102
- | [Oman Air](https://www.omanair.com) ๐Ÿ‡ด๐Ÿ‡ฒ | OM | WY | OMA | oneworld | ![#b39759](https://place-hold.it/10x10/b39759/b39759.png) | โœ… | โœ… | |
103
- | [Pacific Airlines](https://www.pacificairlines.com) | VN | BL | PIC | | ![#324471](https://place-hold.it/10x10/324471/324471.png) | โœ… | โœ… | |
104
- | [Paranair](https://www.paranair.com) | PY | ZP | AZP | | ![#0037a2](https://place-hold.it/10x10/0037a2/0037a2.png) | โœ… | โœ… | |
105
- | [Peach Aviation](https://www.flypeach.com) | JP | MM | APJ | | ![#9b308c](https://place-hold.it/10x10/9b308c/9b308c.png) | | โœ… | |
106
- | [Pegasus Airlines](https://www.flypgs.com) | TR | PC | PGT | | ![#fdb913](https://place-hold.it/10x10/fdb913/fdb913.png) | โœ… | โœ… | |
107
- | [Philippine Airlines](https://www.philippineairlines.com) ๐Ÿ‡ต๐Ÿ‡ญ | PH | PR | PAL | | ![#001f60](https://place-hold.it/10x10/001f60/001f60.png) | โœ… | โœ… | |
108
- | [Qantas](https://www.qantas.com) ๐Ÿ‡ฆ๐Ÿ‡บ | AU | QF | QFA | oneworld | ![#e40000](https://place-hold.it/10x10/e40000/e40000.png) | โ˜‘๏ธ | โœ… | |
109
- | [Qatar Airways](https://www.qatarairways.com) ๐Ÿ‡ถ๐Ÿ‡ฆ | QA | QR | QTR | oneworld | ![#662046](https://place-hold.it/10x10/662046/662046.png) | โœ… | โœ… | |
110
- | [Riyadh Air](https://www.riyadhair.com) ๐Ÿ‡ธ๐Ÿ‡ฆ | SA | RX | RXI | | ![#240852](https://place-hold.it/10x10/240852/240852.png) | โœ… | โœ… | |
111
- | [Royal Air Maroc](https://www.royalairmaroc.com) ๐Ÿ‡ฒ๐Ÿ‡ฆ | MA | AT | RAM | SkyTeam | ![#c20831](https://place-hold.it/10x10/c20831/c20831.png) | โœ… | โœ… | โ˜‘๏ธ |
112
- | [Royal Brunei Airlines](https://www.flyroyalbrunei.com) | BN | BI | RBA | | ![#ffe600](https://place-hold.it/10x10/ffe600/ffe600.png) | | โ˜‘๏ธ | |
113
- | [Ryanair](https://www.ryanair.com) | IE | FR | RYR | | ![#073590](https://place-hold.it/10x10/073590/073590.png) | โœ… | โœ… | |
114
- | [Saudia](https://www.saudia.com) ๐Ÿ‡ธ๐Ÿ‡ฆ | SA | SV | SVA | SkyTeam | ![#0a6937](https://place-hold.it/10x10/0a6937/0a6937.png) | โœ… | โœ… | |
115
- | [Scandinavian Airlines](https://www.flysas.com) ๐Ÿ‡ฉ๐Ÿ‡ฐ ๐Ÿ‡ณ๐Ÿ‡ด ๐Ÿ‡ธ๐Ÿ‡ช | DK,NO,SE | SK | SAS | Star Alliance | ![#000099](https://place-hold.it/10x10/000099/000099.png) | | โœ… | |
116
- | SAS Connect | IE | SL | SZS | | ![#000099](https://place-hold.it/10x10/000099/000099.png) | | โœ… | |
117
- | [Scoot](https://www.flyscoot.com) | SG | TR | TGW | Value Alliance | ![#ffe900](https://place-hold.it/10x10/ffe900/ffe900.png) | | โ˜‘๏ธ | |
118
- | [Singapore Airlines](https://www.singaporeair.com) ๐Ÿ‡ธ๐Ÿ‡ฌ | SG | SQ | SIA | Star Alliance | ![#232f67](https://place-hold.it/10x10/232f67/232f67.png) | โœ… | โœ… | |
119
- | [Southwest Airlines](https://www.southwest.com) | US | WN | SWA | | ![#304cb2](https://place-hold.it/10x10/304cb2/304cb2.png) | โ˜‘๏ธ | โ˜‘๏ธ | |
120
- | [Spirit Airlines](https://www.spirit.com) | US | NK | NKS | | ![#ffec00](https://place-hold.it/10x10/ffec00/ffec00.png) | | โœ… | |
121
- | [Starlux Airlines](https://www.starlux-airlines.com) | TW | JX | SJX | | ![#996a4e](https://place-hold.it/10x10/996a4e/996a4e.png) | โœ… | โœ… | |
122
- | [Sun PhuQuoc Airways](https://www.sunphuquocairways.com) | VN | 9G | SPQ | | ![#ca1c2a](https://place-hold.it/10x10/ca1c2a/ca1c2a.png) | โœ… | โœ… | |
123
- | [SWISS](https://www.swiss.com) ๐Ÿ‡จ๐Ÿ‡ญ | CH | LX | SWR | Star Alliance | ![#cf0a2c](https://place-hold.it/10x10/cf0a2c/cf0a2c.png) | โœ… | โœ… | |
124
- | [T'way Air](https://www.twayair.com) | KR | TW | TWB | | ![#d22c25](https://place-hold.it/10x10/d22c25/d22c25.png) | | โœ… | |
125
- | [TAP Air Portugal](https://www.tapairportugal.com) ๐Ÿ‡ต๐Ÿ‡น | PT | TP | TAP | Star Alliance | ![#bfd730](https://place-hold.it/10x10/bfd730/bfd730.png) | โ˜‘๏ธ | โ˜‘๏ธ | |
126
- | [TAROM](https://www.tarom.ro) ๐Ÿ‡ท๐Ÿ‡ด | RO | RO | ROT | SkyTeam | ![#003366](https://place-hold.it/10x10/003366/003366.png) | โœ… | โœ… | โœ… |
127
- | [Thai Airways](https://www.thaiairways.com) ๐Ÿ‡น๐Ÿ‡ญ | TH | TG | THA | Star Alliance | ![#330066](https://place-hold.it/10x10/330066/330066.png) | โ˜‘๏ธ | โœ… | |
128
- | [Transavia](https://www.transavia.com) | NL | HV | TRA | | ![#00d66c](https://place-hold.it/10x10/00d66c/00d66c.png) | โœ… | โœ… | |
129
- | [Turkish Airlines](https://www.turkishairlines.com) ๐Ÿ‡น๐Ÿ‡ท | TR | TK | THY | Star Alliance | ![#c90019](https://place-hold.it/10x10/c90019/c90019.png) | โœ… | โœ… | |
130
- | [United Airlines](https://www.united.com) | US | UA | UAL | Star Alliance | ![#1414d2](https://place-hold.it/10x10/1414d2/1414d2.png) | โœ… | โœ… | |
131
- | [VietJet Air](https://www.vietjetair.com) | VN | VJ | VJC | | ![#e12127](https://place-hold.it/10x10/e12127/e12127.png) | | โœ… | |
132
- | [Vietnam Airlines](https://www.vietnamairlines.com) ๐Ÿ‡ป๐Ÿ‡ณ | VN | VN | HVN | SkyTeam | ![#d99e09](https://place-hold.it/10x10/d99e09/d99e09.png) | โœ… | โœ… | |
133
- | [Vietravel Airlines](https://www.vietravelairlines.com) | VN | VU | VAG | | ![#0066b3](https://place-hold.it/10x10/0066b3/0066b3.png) | โœ… | โœ… | |
134
- | [Virgin Atlantic](https://www.virginatlantic.com) | GB | VS | VIR | SkyTeam | ![#da0530](https://place-hold.it/10x10/da0530/da0530.png) | โœ… | โœ… | |
135
- | [Virgin Australia](https://www.virginaustralia.com) | AU | VA | VOZ | | ![#e31e23](https://place-hold.it/10x10/e31e23/e31e23.png) | โœ… | โœ… | |
136
- | [Viva](https://www.vivaaerobus.com) | MX | VB | VIV | | ![#00ae44](https://place-hold.it/10x10/00ae44/00ae44.png) | โœ… | โœ… | |
137
- | [Volaris](https://www.volaris.com) | MX | Y4 | VOI | | ![#5b2d82](https://place-hold.it/10x10/5b2d82/5b2d82.png) | โ˜‘๏ธ | โ˜‘๏ธ | |
138
- | [Volotea](https://www.volotea.com) | ES | V7 | VOE | | ![#ed164a](https://place-hold.it/10x10/ed164a/ed164a.png) | โœ… | โœ… | |
139
- | [Vueling](https://www.vueling.com) | ES | VY | VLG | | ![#ffcc00](https://place-hold.it/10x10/ffcc00/ffcc00.png) | | โ˜‘๏ธ | |
140
- | [WestJet](https://www.westjet.com) | CA | WS | WJA | | ![#00aaa6](https://place-hold.it/10x10/00aaa6/00aaa6.png) | โœ… | โœ… | |
141
- | [Wizz Air](https://www.wizzair.com) | HU | W6 | WZZ | | ![#c6007e](https://place-hold.it/10x10/c6007e/c6007e.png) | | โœ… | |
142
- | Wizz Air Abu Dhabi | AE | 5W | WAZ | | ![#c6007e](https://place-hold.it/10x10/c6007e/c6007e.png) | | โœ… | |
143
- | Wizz Air UK | GB | W9 | WUK | | ![#c6007e](https://place-hold.it/10x10/c6007e/c6007e.png) | | โœ… | |
144
- | [XiamenAir](https://www.xiamenair.com) | CN | MF | CXA | SkyTeam | ![#008cd5](https://place-hold.it/10x10/008cd5/008cd5.png) | โœ… | โœ… | |