computedSfc.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.computedSfc = void 0;
  4. const parseCssClassNames_1 = require("../utils/parseCssClassNames");
  5. const parseCssVars_1 = require("../utils/parseCssVars");
  6. const computeds_1 = require("computeds");
  7. function computedSfc(ts, plugins, fileName, snapshot, parsed) {
  8. const untrackedSnapshot = () => {
  9. (0, computeds_1.pauseTracking)();
  10. const res = snapshot();
  11. (0, computeds_1.resetTracking)();
  12. return res;
  13. };
  14. const template = computedNullableSfcBlock('template', 'html', (0, computeds_1.computed)(() => parsed()?.descriptor.template ?? undefined), (_block, base) => {
  15. const compiledAst = computedTemplateAst(base);
  16. return mergeObject(base, {
  17. get ast() { return compiledAst()?.ast; },
  18. get errors() { return compiledAst()?.errors; },
  19. get warnings() { return compiledAst()?.warnings; },
  20. });
  21. });
  22. const script = computedNullableSfcBlock('script', 'js', (0, computeds_1.computed)(() => parsed()?.descriptor.script ?? undefined), (block, base) => {
  23. const src = (0, computeds_1.computed)(() => block().src);
  24. const srcOffset = (0, computeds_1.computed)(() => {
  25. const _src = src();
  26. return _src ? untrackedSnapshot().getText(0, base.startTagEnd).lastIndexOf(_src) - base.startTagEnd : -1;
  27. });
  28. const ast = (0, computeds_1.computed)(() => ts.createSourceFile(fileName + '.' + base.lang, base.content, ts.ScriptTarget.Latest));
  29. return mergeObject(base, {
  30. get src() { return src(); },
  31. get srcOffset() { return srcOffset(); },
  32. get ast() { return ast(); },
  33. });
  34. });
  35. const scriptSetup = computedNullableSfcBlock('scriptSetup', 'js', (0, computeds_1.computed)(() => parsed()?.descriptor.scriptSetup ?? undefined), (block, base) => {
  36. const generic = (0, computeds_1.computed)(() => {
  37. const _block = block();
  38. return typeof _block.attrs.generic === 'string' ? _block.attrs.generic : undefined;
  39. });
  40. const genericOffset = (0, computeds_1.computed)(() => {
  41. const _generic = generic();
  42. return _generic !== undefined ? untrackedSnapshot().getText(0, base.startTagEnd).lastIndexOf(_generic) - base.startTagEnd : -1;
  43. });
  44. const ast = (0, computeds_1.computed)(() => ts.createSourceFile(fileName + '.' + base.lang, base.content, ts.ScriptTarget.Latest));
  45. return mergeObject(base, {
  46. get generic() { return generic(); },
  47. get genericOffset() { return genericOffset(); },
  48. get ast() { return ast(); },
  49. });
  50. });
  51. const styles = (0, computeds_1.computedArray)((0, computeds_1.computed)(() => parsed()?.descriptor.styles ?? []), (block, i) => {
  52. const base = computedSfcBlock('style_' + i, 'css', block);
  53. const module = (0, computeds_1.computed)(() => typeof block().module === 'string' ? block().module : block().module ? '$style' : undefined);
  54. const scoped = (0, computeds_1.computed)(() => !!block().scoped);
  55. const cssVars = (0, computeds_1.computed)(() => [...(0, parseCssVars_1.parseCssVars)(base.content)]);
  56. const classNames = (0, computeds_1.computed)(() => [...(0, parseCssClassNames_1.parseCssClassNames)(base.content)]);
  57. return (0, computeds_1.computed)(() => mergeObject(base, {
  58. get module() { return module(); },
  59. get scoped() { return scoped(); },
  60. get cssVars() { return cssVars(); },
  61. get classNames() { return classNames(); },
  62. }));
  63. });
  64. const customBlocks = (0, computeds_1.computedArray)((0, computeds_1.computed)(() => parsed()?.descriptor.customBlocks ?? []), (block, i) => {
  65. const base = computedSfcBlock('customBlock_' + i, 'txt', block);
  66. const type = (0, computeds_1.computed)(() => block().type);
  67. return (0, computeds_1.computed)(() => mergeObject(base, {
  68. get type() { return type(); },
  69. }));
  70. });
  71. return {
  72. get template() { return template(); },
  73. get script() { return script(); },
  74. get scriptSetup() { return scriptSetup(); },
  75. get styles() { return styles; },
  76. get customBlocks() { return customBlocks; },
  77. get templateAst() { return template()?.ast; },
  78. get scriptAst() { return script()?.ast; },
  79. get scriptSetupAst() { return scriptSetup()?.ast; },
  80. };
  81. function computedTemplateAst(base) {
  82. let cache;
  83. return (0, computeds_1.computed)(() => {
  84. if (cache?.template === base.content) {
  85. return {
  86. errors: [],
  87. warnings: [],
  88. ast: cache?.result.ast,
  89. };
  90. }
  91. // incremental update
  92. if (cache?.plugin.updateSFCTemplate) {
  93. const change = untrackedSnapshot().getChangeRange(cache.snapshot);
  94. if (change) {
  95. (0, computeds_1.pauseTracking)();
  96. const templateOffset = base.startTagEnd;
  97. (0, computeds_1.resetTracking)();
  98. const newText = untrackedSnapshot().getText(change.span.start, change.span.start + change.newLength);
  99. const newResult = cache.plugin.updateSFCTemplate(cache.result, {
  100. start: change.span.start - templateOffset,
  101. end: change.span.start + change.span.length - templateOffset,
  102. newText,
  103. });
  104. if (newResult) {
  105. cache.template = base.content;
  106. cache.snapshot = untrackedSnapshot();
  107. cache.result = newResult;
  108. return {
  109. errors: [],
  110. warnings: [],
  111. ast: newResult.ast,
  112. };
  113. }
  114. }
  115. }
  116. const errors = [];
  117. const warnings = [];
  118. let options = {
  119. onError: (err) => errors.push(err),
  120. onWarn: (err) => warnings.push(err),
  121. expressionPlugins: ['typescript'],
  122. };
  123. for (const plugin of plugins) {
  124. if (plugin.resolveTemplateCompilerOptions) {
  125. options = plugin.resolveTemplateCompilerOptions(options);
  126. }
  127. }
  128. for (const plugin of plugins) {
  129. let result;
  130. try {
  131. result = plugin.compileSFCTemplate?.(base.lang, base.content, options);
  132. }
  133. catch (e) {
  134. const err = e;
  135. errors.push(err);
  136. }
  137. if (result || errors.length) {
  138. if (result && !errors.length && !warnings.length) {
  139. cache = {
  140. template: base.content,
  141. snapshot: untrackedSnapshot(),
  142. result: result,
  143. plugin,
  144. };
  145. }
  146. else {
  147. cache = undefined;
  148. }
  149. return {
  150. errors,
  151. warnings,
  152. ast: result?.ast,
  153. };
  154. }
  155. }
  156. return {
  157. errors,
  158. warnings,
  159. ast: undefined,
  160. };
  161. });
  162. }
  163. function computedNullableSfcBlock(name, defaultLang, block, resolve) {
  164. const hasBlock = (0, computeds_1.computed)(() => !!block());
  165. return (0, computeds_1.computed)(() => {
  166. if (!hasBlock()) {
  167. return;
  168. }
  169. const _block = (0, computeds_1.computed)(() => block());
  170. return resolve(_block, computedSfcBlock(name, defaultLang, _block));
  171. });
  172. }
  173. function computedSfcBlock(name, defaultLang, block) {
  174. const lang = (0, computeds_1.computed)(() => block().lang ?? defaultLang);
  175. const attrs = (0, computeds_1.computed)(() => block().attrs); // TODO: computed it
  176. const content = (0, computeds_1.computed)(() => block().content);
  177. const startTagEnd = (0, computeds_1.computed)(() => block().loc.start.offset);
  178. const endTagStart = (0, computeds_1.computed)(() => block().loc.end.offset);
  179. const start = (0, computeds_1.computed)(() => untrackedSnapshot().getText(0, startTagEnd()).lastIndexOf('<' + block().type));
  180. const end = (0, computeds_1.computed)(() => endTagStart() + untrackedSnapshot().getText(endTagStart(), untrackedSnapshot().getLength()).indexOf('>') + 1);
  181. return {
  182. name,
  183. get lang() { return lang(); },
  184. get attrs() { return attrs(); },
  185. get content() { return content(); },
  186. get startTagEnd() { return startTagEnd(); },
  187. get endTagStart() { return endTagStart(); },
  188. get start() { return start(); },
  189. get end() { return end(); },
  190. };
  191. }
  192. }
  193. exports.computedSfc = computedSfc;
  194. function mergeObject(a, b) {
  195. return Object.defineProperties(a, Object.getOwnPropertyDescriptors(b));
  196. }
  197. //# sourceMappingURL=computedSfc.js.map