create.js 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  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. function processChunk(chunk) {
  20. tokenize(chunk, (type, start, end) => {
  21. this.token(type, chunk.slice(start, end));
  22. });
  23. }
  24. export function createGenerator(config) {
  25. const types = new Map();
  26. for (let [name, item] of Object.entries(config.node)) {
  27. const fn = item.generate || item;
  28. if (typeof fn === 'function') {
  29. types.set(name, item.generate || item);
  30. }
  31. }
  32. return function(node, options) {
  33. let buffer = '';
  34. let prevCode = 0;
  35. let handlers = {
  36. node(node) {
  37. if (types.has(node.type)) {
  38. types.get(node.type).call(publicApi, node);
  39. } else {
  40. throw new Error('Unknown node type: ' + node.type);
  41. }
  42. },
  43. tokenBefore: tokenBefore.safe,
  44. token(type, value) {
  45. prevCode = this.tokenBefore(prevCode, type, value);
  46. this.emit(value, type, false);
  47. if (type === Delim && value.charCodeAt(0) === REVERSESOLIDUS) {
  48. this.emit('\n', WhiteSpace, true);
  49. }
  50. },
  51. emit(value) {
  52. buffer += value;
  53. },
  54. result() {
  55. return buffer;
  56. }
  57. };
  58. if (options) {
  59. if (typeof options.decorator === 'function') {
  60. handlers = options.decorator(handlers);
  61. }
  62. if (options.sourceMap) {
  63. handlers = generateSourceMap(handlers);
  64. }
  65. if (options.mode in tokenBefore) {
  66. handlers.tokenBefore = tokenBefore[options.mode];
  67. }
  68. }
  69. const publicApi = {
  70. node: (node) => handlers.node(node),
  71. children: processChildren,
  72. token: (type, value) => handlers.token(type, value),
  73. tokenize: processChunk
  74. };
  75. handlers.node(node);
  76. return handlers.result();
  77. };
  78. };