create.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. import { tokenize, Delim, WhiteSpace } from '../tokenizer/index.js';
  2. import { generateSourceMap } from './sourceMap.js';
  3. import * as tokenBefore from './token-before.js';
  4. const REVERSESOLIDUS = 0x005c; // U+005C REVERSE SOLIDUS (\)
  5. function processChildren(node, delimeter) {
  6. if (typeof delimeter === 'function') {
  7. let prev = null;
  8. node.children.forEach(node => {
  9. if (prev !== null) {
  10. delimeter.call(this, prev);
  11. }
  12. this.node(node);
  13. prev = node;
  14. });
  15. return;
  16. }
  17. node.children.forEach(this.node, this);
  18. }
  19. export function createGenerator(config) {
  20. const types = new Map();
  21. for (let [name, item] of Object.entries(config.node)) {
  22. const fn = item.generate || item;
  23. if (typeof fn === 'function') {
  24. types.set(name, item.generate || item);
  25. }
  26. }
  27. return function(node, options) {
  28. let buffer = '';
  29. let prevCode = 0;
  30. let handlers = {
  31. node(node) {
  32. if (types.has(node.type)) {
  33. types.get(node.type).call(publicApi, node);
  34. } else {
  35. throw new Error('Unknown node type: ' + node.type);
  36. }
  37. },
  38. tokenBefore: tokenBefore.safe,
  39. token(type, value, suppressAutoWhiteSpace) {
  40. prevCode = this.tokenBefore(prevCode, type, value);
  41. if (!suppressAutoWhiteSpace && prevCode & 1) {
  42. this.emit(' ', WhiteSpace, true);
  43. }
  44. this.emit(value, type, false);
  45. if (type === Delim && value.charCodeAt(0) === REVERSESOLIDUS) {
  46. this.emit('\n', WhiteSpace, true);
  47. }
  48. },
  49. emit(value) {
  50. buffer += value;
  51. },
  52. result() {
  53. return buffer;
  54. }
  55. };
  56. if (options) {
  57. if (typeof options.decorator === 'function') {
  58. handlers = options.decorator(handlers);
  59. }
  60. if (options.sourceMap) {
  61. handlers = generateSourceMap(handlers);
  62. }
  63. if (options.mode in tokenBefore) {
  64. handlers.tokenBefore = tokenBefore[options.mode];
  65. }
  66. }
  67. const publicApi = {
  68. node: (node) => handlers.node(node),
  69. children: processChildren,
  70. token: (type, value) => handlers.token(type, value),
  71. tokenize: (raw) =>
  72. tokenize(raw, (type, start, end) => {
  73. handlers.token(
  74. type,
  75. raw.slice(start, end),
  76. start !== 0 // suppress auto whitespace for internal value tokens
  77. );
  78. })
  79. };
  80. handlers.node(node);
  81. return handlers.result();
  82. };
  83. };