parse-atlas.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. // reference:
  2. // http://zh.esotericsoftware.com/spine-atlas-format
  3. const { extname } = require('path');
  4. const { existsSync, createReadStream } = require('fs');
  5. const { createInterface } = require('readline');
  6. function isNumber(value) {
  7. return !isNaN(Number(value));
  8. }
  9. function isBoolean(value) {
  10. return /^(true)|(false)$/.test(value);
  11. }
  12. function formatValue(value) {
  13. const _value = String(value).trim();
  14. if (/^(undefined)|(null)|(NaN)$/.test(_value)) {
  15. return _value;
  16. }
  17. if (_value === '') {
  18. return null;
  19. }
  20. const list = _value.split(',').map(v => {
  21. v = v.trim();
  22. if (v === '') {
  23. return null;
  24. }
  25. if (isNumber(v)) {
  26. return Number(v);
  27. }
  28. if (isBoolean(v)) {
  29. return Boolean(v);
  30. }
  31. return v;
  32. });
  33. if (list.length === 1) {
  34. return list[0];
  35. }
  36. return list;
  37. }
  38. const imageExt = ['.png', '.jpg', '.jpeg', '.webp', '.gif'];
  39. const pageAttr = ['name', 'size', 'format', 'filter', 'repeat', 'pma'];
  40. class ParseAtlasFile {
  41. constructor() { }
  42. #json = {};
  43. #lockPage = false;
  44. #currentPage = '';
  45. #currentRegin = '';
  46. reset() {
  47. this.#json = {};
  48. this.#lockPage = false;
  49. this.#currentPage = '';
  50. this.#currentRegin = '';
  51. }
  52. #push(line) {
  53. const string = line.trim();
  54. const isEmptyLine = string.length === 0;
  55. const isPage = imageExt.includes(extname(line)); // the begin of page
  56. if (isEmptyLine) {
  57. // if is a empty line,we need create a new page.
  58. this.#lockPage = false;
  59. return;
  60. }
  61. if (isPage && !this.#lockPage) {
  62. this.#json[line] = {};
  63. this.#lockPage = true;
  64. this.#currentPage = line;
  65. this.#currentRegin = '';
  66. } else {
  67. const keyValue = line.split(':');
  68. const key = keyValue[0].trim();
  69. const value = formatValue(keyValue[1]);
  70. // Check if it's region
  71. const isRegion = keyValue.length === 1;
  72. if (isRegion) {
  73. this.#currentRegin = key;
  74. this.#json[this.#currentPage][this.#currentRegin] = {};
  75. } else {
  76. if (this.#currentRegin) {
  77. this.#json[this.#currentPage][this.#currentRegin][key] = value;
  78. } else {
  79. if (!pageAttr.includes(key)) {
  80. console.error(`${key} is not a valid attribute, Please check!`);
  81. }
  82. this.#json[this.#currentPage][key] = value;
  83. }
  84. }
  85. }
  86. }
  87. parse(atlasFile) {
  88. this.reset();
  89. return new Promise((resolve, reject) => {
  90. if (typeof atlasFile !== 'string') {
  91. reject(new Error('File path must be a string'));
  92. }
  93. if (extname(atlasFile) !== '.atlas') {
  94. reject(new Error('The file does not a .atlas'));
  95. }
  96. if (!existsSync(atlasFile)) {
  97. reject(new Error('The file does not exist'));
  98. }
  99. const readStream = createReadStream(atlasFile, {
  100. encoding: 'utf-8',
  101. });
  102. const readLineStream = createInterface({
  103. input: readStream,
  104. setEncoding: 'utf-8',
  105. });
  106. readLineStream.on('line', (line) => {
  107. this.#push(line);
  108. });
  109. readLineStream.on('close', (err) => {
  110. if (err) {
  111. reject(err);
  112. }
  113. resolve(this.#json);
  114. });
  115. });
  116. }
  117. }
  118. exports.ParseAtlasFile = ParseAtlasFile;