@depup/svelte 5.53.3-depup.0

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 (386) hide show
  1. package/LICENSE.md +7 -0
  2. package/README.md +41 -0
  3. package/action.d.ts +1 -0
  4. package/animate.d.ts +1 -0
  5. package/compiler/index.js +1 -0
  6. package/compiler/package.json +3 -0
  7. package/compiler.d.ts +1 -0
  8. package/easing.d.ts +1 -0
  9. package/elements.d.ts +2078 -0
  10. package/index.d.ts +1 -0
  11. package/legacy.d.ts +1 -0
  12. package/motion.d.ts +1 -0
  13. package/package.json +185 -0
  14. package/src/animate/index.js +78 -0
  15. package/src/attachments/index.js +113 -0
  16. package/src/compiler/errors.js +1719 -0
  17. package/src/compiler/index.js +198 -0
  18. package/src/compiler/legacy.js +637 -0
  19. package/src/compiler/migrate/index.js +1996 -0
  20. package/src/compiler/phases/1-parse/acorn.js +198 -0
  21. package/src/compiler/phases/1-parse/index.js +326 -0
  22. package/src/compiler/phases/1-parse/read/context.js +116 -0
  23. package/src/compiler/phases/1-parse/read/expression.js +93 -0
  24. package/src/compiler/phases/1-parse/read/options.js +263 -0
  25. package/src/compiler/phases/1-parse/read/script.js +97 -0
  26. package/src/compiler/phases/1-parse/read/style.js +637 -0
  27. package/src/compiler/phases/1-parse/remove_typescript_nodes.js +180 -0
  28. package/src/compiler/phases/1-parse/state/element.js +937 -0
  29. package/src/compiler/phases/1-parse/state/fragment.js +17 -0
  30. package/src/compiler/phases/1-parse/state/tag.js +751 -0
  31. package/src/compiler/phases/1-parse/state/text.js +23 -0
  32. package/src/compiler/phases/1-parse/utils/bracket.js +213 -0
  33. package/src/compiler/phases/1-parse/utils/create.js +16 -0
  34. package/src/compiler/phases/1-parse/utils/entities.js +2234 -0
  35. package/src/compiler/phases/1-parse/utils/fuzzymatch.js +281 -0
  36. package/src/compiler/phases/1-parse/utils/html.js +127 -0
  37. package/src/compiler/phases/2-analyze/css/css-analyze.js +331 -0
  38. package/src/compiler/phases/2-analyze/css/css-prune.js +1206 -0
  39. package/src/compiler/phases/2-analyze/css/css-warn.js +47 -0
  40. package/src/compiler/phases/2-analyze/css/utils.js +177 -0
  41. package/src/compiler/phases/2-analyze/index.js +1300 -0
  42. package/src/compiler/phases/2-analyze/utils/check_graph_for_cycles.js +47 -0
  43. package/src/compiler/phases/2-analyze/visitors/AnimateDirective.js +15 -0
  44. package/src/compiler/phases/2-analyze/visitors/ArrowFunctionExpression.js +11 -0
  45. package/src/compiler/phases/2-analyze/visitors/AssignmentExpression.js +31 -0
  46. package/src/compiler/phases/2-analyze/visitors/AttachTag.js +17 -0
  47. package/src/compiler/phases/2-analyze/visitors/Attribute.js +66 -0
  48. package/src/compiler/phases/2-analyze/visitors/AwaitBlock.js +48 -0
  49. package/src/compiler/phases/2-analyze/visitors/AwaitExpression.js +150 -0
  50. package/src/compiler/phases/2-analyze/visitors/BindDirective.js +280 -0
  51. package/src/compiler/phases/2-analyze/visitors/CallExpression.js +339 -0
  52. package/src/compiler/phases/2-analyze/visitors/ClassBody.js +156 -0
  53. package/src/compiler/phases/2-analyze/visitors/ClassDeclaration.js +25 -0
  54. package/src/compiler/phases/2-analyze/visitors/ClassDirective.js +13 -0
  55. package/src/compiler/phases/2-analyze/visitors/Component.js +26 -0
  56. package/src/compiler/phases/2-analyze/visitors/ConstTag.js +45 -0
  57. package/src/compiler/phases/2-analyze/visitors/DebugTag.js +15 -0
  58. package/src/compiler/phases/2-analyze/visitors/EachBlock.js +97 -0
  59. package/src/compiler/phases/2-analyze/visitors/ExportDefaultDeclaration.js +20 -0
  60. package/src/compiler/phases/2-analyze/visitors/ExportNamedDeclaration.js +70 -0
  61. package/src/compiler/phases/2-analyze/visitors/ExportSpecifier.js +30 -0
  62. package/src/compiler/phases/2-analyze/visitors/ExpressionStatement.js +38 -0
  63. package/src/compiler/phases/2-analyze/visitors/ExpressionTag.js +26 -0
  64. package/src/compiler/phases/2-analyze/visitors/Fragment.js +10 -0
  65. package/src/compiler/phases/2-analyze/visitors/FunctionDeclaration.js +16 -0
  66. package/src/compiler/phases/2-analyze/visitors/FunctionExpression.js +11 -0
  67. package/src/compiler/phases/2-analyze/visitors/HtmlTag.js +19 -0
  68. package/src/compiler/phases/2-analyze/visitors/Identifier.js +194 -0
  69. package/src/compiler/phases/2-analyze/visitors/IfBlock.js +46 -0
  70. package/src/compiler/phases/2-analyze/visitors/ImportDeclaration.js +31 -0
  71. package/src/compiler/phases/2-analyze/visitors/KeyBlock.js +21 -0
  72. package/src/compiler/phases/2-analyze/visitors/LabeledStatement.js +95 -0
  73. package/src/compiler/phases/2-analyze/visitors/LetDirective.js +24 -0
  74. package/src/compiler/phases/2-analyze/visitors/Literal.js +14 -0
  75. package/src/compiler/phases/2-analyze/visitors/MemberExpression.js +28 -0
  76. package/src/compiler/phases/2-analyze/visitors/NewExpression.js +17 -0
  77. package/src/compiler/phases/2-analyze/visitors/OnDirective.js +28 -0
  78. package/src/compiler/phases/2-analyze/visitors/PropertyDefinition.js +21 -0
  79. package/src/compiler/phases/2-analyze/visitors/RegularElement.js +240 -0
  80. package/src/compiler/phases/2-analyze/visitors/RenderTag.js +68 -0
  81. package/src/compiler/phases/2-analyze/visitors/SlotElement.js +42 -0
  82. package/src/compiler/phases/2-analyze/visitors/SnippetBlock.js +113 -0
  83. package/src/compiler/phases/2-analyze/visitors/SpreadAttribute.js +13 -0
  84. package/src/compiler/phases/2-analyze/visitors/SpreadElement.js +16 -0
  85. package/src/compiler/phases/2-analyze/visitors/StyleDirective.js +39 -0
  86. package/src/compiler/phases/2-analyze/visitors/SvelteBody.js +22 -0
  87. package/src/compiler/phases/2-analyze/visitors/SvelteBoundary.js +30 -0
  88. package/src/compiler/phases/2-analyze/visitors/SvelteComponent.js +18 -0
  89. package/src/compiler/phases/2-analyze/visitors/SvelteDocument.js +24 -0
  90. package/src/compiler/phases/2-analyze/visitors/SvelteElement.js +78 -0
  91. package/src/compiler/phases/2-analyze/visitors/SvelteFragment.js +27 -0
  92. package/src/compiler/phases/2-analyze/visitors/SvelteHead.js +18 -0
  93. package/src/compiler/phases/2-analyze/visitors/SvelteSelf.js +36 -0
  94. package/src/compiler/phases/2-analyze/visitors/SvelteWindow.js +24 -0
  95. package/src/compiler/phases/2-analyze/visitors/TaggedTemplateExpression.js +16 -0
  96. package/src/compiler/phases/2-analyze/visitors/TemplateElement.js +12 -0
  97. package/src/compiler/phases/2-analyze/visitors/Text.js +52 -0
  98. package/src/compiler/phases/2-analyze/visitors/TitleElement.js +21 -0
  99. package/src/compiler/phases/2-analyze/visitors/TransitionDirective.js +19 -0
  100. package/src/compiler/phases/2-analyze/visitors/UpdateExpression.js +29 -0
  101. package/src/compiler/phases/2-analyze/visitors/UseDirective.js +18 -0
  102. package/src/compiler/phases/2-analyze/visitors/VariableDeclarator.js +160 -0
  103. package/src/compiler/phases/2-analyze/visitors/shared/a11y/constants.js +334 -0
  104. package/src/compiler/phases/2-analyze/visitors/shared/a11y/index.js +981 -0
  105. package/src/compiler/phases/2-analyze/visitors/shared/attribute.js +125 -0
  106. package/src/compiler/phases/2-analyze/visitors/shared/component.js +177 -0
  107. package/src/compiler/phases/2-analyze/visitors/shared/element.js +160 -0
  108. package/src/compiler/phases/2-analyze/visitors/shared/fragment.js +15 -0
  109. package/src/compiler/phases/2-analyze/visitors/shared/function.js +24 -0
  110. package/src/compiler/phases/2-analyze/visitors/shared/snippets.js +17 -0
  111. package/src/compiler/phases/2-analyze/visitors/shared/special-element.js +16 -0
  112. package/src/compiler/phases/2-analyze/visitors/shared/utils.js +301 -0
  113. package/src/compiler/phases/3-transform/client/transform-client.js +719 -0
  114. package/src/compiler/phases/3-transform/client/transform-template/fix-attribute-casing.js +18 -0
  115. package/src/compiler/phases/3-transform/client/transform-template/index.js +67 -0
  116. package/src/compiler/phases/3-transform/client/transform-template/template.js +164 -0
  117. package/src/compiler/phases/3-transform/client/utils.js +181 -0
  118. package/src/compiler/phases/3-transform/client/visitors/AnimateDirective.js +38 -0
  119. package/src/compiler/phases/3-transform/client/visitors/ArrowFunctionExpression.js +11 -0
  120. package/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js +247 -0
  121. package/src/compiler/phases/3-transform/client/visitors/AttachTag.js +26 -0
  122. package/src/compiler/phases/3-transform/client/visitors/Attribute.js +14 -0
  123. package/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js +124 -0
  124. package/src/compiler/phases/3-transform/client/visitors/AwaitExpression.js +25 -0
  125. package/src/compiler/phases/3-transform/client/visitors/BinaryExpression.js +34 -0
  126. package/src/compiler/phases/3-transform/client/visitors/BindDirective.js +290 -0
  127. package/src/compiler/phases/3-transform/client/visitors/BlockStatement.js +32 -0
  128. package/src/compiler/phases/3-transform/client/visitors/BreakStatement.js +20 -0
  129. package/src/compiler/phases/3-transform/client/visitors/CallExpression.js +136 -0
  130. package/src/compiler/phases/3-transform/client/visitors/ClassBody.js +111 -0
  131. package/src/compiler/phases/3-transform/client/visitors/Comment.js +11 -0
  132. package/src/compiler/phases/3-transform/client/visitors/Component.js +12 -0
  133. package/src/compiler/phases/3-transform/client/visitors/ConstTag.js +134 -0
  134. package/src/compiler/phases/3-transform/client/visitors/DebugTag.js +28 -0
  135. package/src/compiler/phases/3-transform/client/visitors/EachBlock.js +362 -0
  136. package/src/compiler/phases/3-transform/client/visitors/ExportNamedDeclaration.js +19 -0
  137. package/src/compiler/phases/3-transform/client/visitors/ExpressionStatement.js +20 -0
  138. package/src/compiler/phases/3-transform/client/visitors/ForOfStatement.js +25 -0
  139. package/src/compiler/phases/3-transform/client/visitors/Fragment.js +186 -0
  140. package/src/compiler/phases/3-transform/client/visitors/FunctionDeclaration.js +12 -0
  141. package/src/compiler/phases/3-transform/client/visitors/FunctionExpression.js +11 -0
  142. package/src/compiler/phases/3-transform/client/visitors/HtmlTag.js +53 -0
  143. package/src/compiler/phases/3-transform/client/visitors/Identifier.js +45 -0
  144. package/src/compiler/phases/3-transform/client/visitors/IfBlock.js +131 -0
  145. package/src/compiler/phases/3-transform/client/visitors/KeyBlock.js +45 -0
  146. package/src/compiler/phases/3-transform/client/visitors/LabeledStatement.js +64 -0
  147. package/src/compiler/phases/3-transform/client/visitors/LetDirective.js +55 -0
  148. package/src/compiler/phases/3-transform/client/visitors/MemberExpression.js +23 -0
  149. package/src/compiler/phases/3-transform/client/visitors/OnDirective.js +38 -0
  150. package/src/compiler/phases/3-transform/client/visitors/Program.js +153 -0
  151. package/src/compiler/phases/3-transform/client/visitors/RegularElement.js +725 -0
  152. package/src/compiler/phases/3-transform/client/visitors/RenderTag.js +95 -0
  153. package/src/compiler/phases/3-transform/client/visitors/SlotElement.js +94 -0
  154. package/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js +94 -0
  155. package/src/compiler/phases/3-transform/client/visitors/SpreadAttribute.js +10 -0
  156. package/src/compiler/phases/3-transform/client/visitors/SvelteBody.js +11 -0
  157. package/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js +126 -0
  158. package/src/compiler/phases/3-transform/client/visitors/SvelteComponent.js +13 -0
  159. package/src/compiler/phases/3-transform/client/visitors/SvelteDocument.js +11 -0
  160. package/src/compiler/phases/3-transform/client/visitors/SvelteElement.js +161 -0
  161. package/src/compiler/phases/3-transform/client/visitors/SvelteFragment.js +17 -0
  162. package/src/compiler/phases/3-transform/client/visitors/SvelteHead.js +23 -0
  163. package/src/compiler/phases/3-transform/client/visitors/SvelteSelf.js +13 -0
  164. package/src/compiler/phases/3-transform/client/visitors/SvelteWindow.js +11 -0
  165. package/src/compiler/phases/3-transform/client/visitors/TitleElement.js +48 -0
  166. package/src/compiler/phases/3-transform/client/visitors/TransitionDirective.js +41 -0
  167. package/src/compiler/phases/3-transform/client/visitors/UpdateExpression.js +55 -0
  168. package/src/compiler/phases/3-transform/client/visitors/UseDirective.js +49 -0
  169. package/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js +422 -0
  170. package/src/compiler/phases/3-transform/client/visitors/shared/component.js +536 -0
  171. package/src/compiler/phases/3-transform/client/visitors/shared/declarations.js +53 -0
  172. package/src/compiler/phases/3-transform/client/visitors/shared/element.js +263 -0
  173. package/src/compiler/phases/3-transform/client/visitors/shared/events.js +180 -0
  174. package/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +185 -0
  175. package/src/compiler/phases/3-transform/client/visitors/shared/function.js +17 -0
  176. package/src/compiler/phases/3-transform/client/visitors/shared/special_element.js +22 -0
  177. package/src/compiler/phases/3-transform/client/visitors/shared/utils.js +513 -0
  178. package/src/compiler/phases/3-transform/css/index.js +479 -0
  179. package/src/compiler/phases/3-transform/index.js +118 -0
  180. package/src/compiler/phases/3-transform/server/transform-server.js +428 -0
  181. package/src/compiler/phases/3-transform/server/visitors/AssignmentExpression.js +124 -0
  182. package/src/compiler/phases/3-transform/server/visitors/AwaitBlock.js +36 -0
  183. package/src/compiler/phases/3-transform/server/visitors/AwaitExpression.js +40 -0
  184. package/src/compiler/phases/3-transform/server/visitors/CallExpression.js +71 -0
  185. package/src/compiler/phases/3-transform/server/visitors/ClassBody.js +81 -0
  186. package/src/compiler/phases/3-transform/server/visitors/Component.js +13 -0
  187. package/src/compiler/phases/3-transform/server/visitors/ConstTag.js +49 -0
  188. package/src/compiler/phases/3-transform/server/visitors/DebugTag.js +24 -0
  189. package/src/compiler/phases/3-transform/server/visitors/EachBlock.js +76 -0
  190. package/src/compiler/phases/3-transform/server/visitors/ExpressionStatement.js +23 -0
  191. package/src/compiler/phases/3-transform/server/visitors/Fragment.js +53 -0
  192. package/src/compiler/phases/3-transform/server/visitors/HtmlTag.js +25 -0
  193. package/src/compiler/phases/3-transform/server/visitors/Identifier.js +24 -0
  194. package/src/compiler/phases/3-transform/server/visitors/IfBlock.js +48 -0
  195. package/src/compiler/phases/3-transform/server/visitors/KeyBlock.js +22 -0
  196. package/src/compiler/phases/3-transform/server/visitors/LabeledStatement.js +24 -0
  197. package/src/compiler/phases/3-transform/server/visitors/MemberExpression.js +19 -0
  198. package/src/compiler/phases/3-transform/server/visitors/Program.js +25 -0
  199. package/src/compiler/phases/3-transform/server/visitors/PropertyDefinition.js +37 -0
  200. package/src/compiler/phases/3-transform/server/visitors/RegularElement.js +216 -0
  201. package/src/compiler/phases/3-transform/server/visitors/RenderTag.js +45 -0
  202. package/src/compiler/phases/3-transform/server/visitors/SlotElement.js +68 -0
  203. package/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js +29 -0
  204. package/src/compiler/phases/3-transform/server/visitors/SpreadAttribute.js +10 -0
  205. package/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js +139 -0
  206. package/src/compiler/phases/3-transform/server/visitors/SvelteComponent.js +12 -0
  207. package/src/compiler/phases/3-transform/server/visitors/SvelteElement.js +89 -0
  208. package/src/compiler/phases/3-transform/server/visitors/SvelteFragment.js +11 -0
  209. package/src/compiler/phases/3-transform/server/visitors/SvelteHead.js +25 -0
  210. package/src/compiler/phases/3-transform/server/visitors/SvelteSelf.js +12 -0
  211. package/src/compiler/phases/3-transform/server/visitors/TitleElement.js +21 -0
  212. package/src/compiler/phases/3-transform/server/visitors/UpdateExpression.js +35 -0
  213. package/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js +247 -0
  214. package/src/compiler/phases/3-transform/server/visitors/shared/component.js +359 -0
  215. package/src/compiler/phases/3-transform/server/visitors/shared/element.js +557 -0
  216. package/src/compiler/phases/3-transform/server/visitors/shared/utils.js +408 -0
  217. package/src/compiler/phases/3-transform/shared/assignments.js +92 -0
  218. package/src/compiler/phases/3-transform/shared/transform-async.js +114 -0
  219. package/src/compiler/phases/3-transform/utils.js +451 -0
  220. package/src/compiler/phases/bindings.js +227 -0
  221. package/src/compiler/phases/css.js +14 -0
  222. package/src/compiler/phases/nodes.js +258 -0
  223. package/src/compiler/phases/patterns.js +27 -0
  224. package/src/compiler/phases/scope.js +1432 -0
  225. package/src/compiler/preprocess/decode_sourcemap.js +96 -0
  226. package/src/compiler/preprocess/index.js +368 -0
  227. package/src/compiler/preprocess/replace_in_code.js +72 -0
  228. package/src/compiler/print/index.js +911 -0
  229. package/src/compiler/state.js +144 -0
  230. package/src/compiler/utils/assert.js +9 -0
  231. package/src/compiler/utils/ast.js +639 -0
  232. package/src/compiler/utils/builders.js +698 -0
  233. package/src/compiler/utils/compile_diagnostic.js +107 -0
  234. package/src/compiler/utils/extract_svelte_ignore.js +104 -0
  235. package/src/compiler/utils/mapped_code.js +454 -0
  236. package/src/compiler/utils/push_array.js +13 -0
  237. package/src/compiler/utils/sanitize_template_string.js +7 -0
  238. package/src/compiler/utils/slot.js +20 -0
  239. package/src/compiler/utils/string.js +9 -0
  240. package/src/compiler/validate-options.js +324 -0
  241. package/src/compiler/warnings.js +845 -0
  242. package/src/constants.js +66 -0
  243. package/src/easing/index.js +286 -0
  244. package/src/escaping.js +26 -0
  245. package/src/events/index.js +1 -0
  246. package/src/html-tree-validation.js +238 -0
  247. package/src/index-client.js +255 -0
  248. package/src/index-server.js +56 -0
  249. package/src/internal/client/constants.js +77 -0
  250. package/src/internal/client/context.js +258 -0
  251. package/src/internal/client/dev/assign.js +79 -0
  252. package/src/internal/client/dev/console-log.js +37 -0
  253. package/src/internal/client/dev/css.js +31 -0
  254. package/src/internal/client/dev/debug.js +500 -0
  255. package/src/internal/client/dev/elements.js +63 -0
  256. package/src/internal/client/dev/equality.js +101 -0
  257. package/src/internal/client/dev/hmr.js +89 -0
  258. package/src/internal/client/dev/inspect.js +72 -0
  259. package/src/internal/client/dev/legacy.js +25 -0
  260. package/src/internal/client/dev/ownership.js +81 -0
  261. package/src/internal/client/dev/tracing.js +162 -0
  262. package/src/internal/client/dev/validation.js +16 -0
  263. package/src/internal/client/dom/blocks/async.js +71 -0
  264. package/src/internal/client/dom/blocks/await.js +142 -0
  265. package/src/internal/client/dom/blocks/boundary.js +534 -0
  266. package/src/internal/client/dom/blocks/branches.js +227 -0
  267. package/src/internal/client/dom/blocks/css-props.js +28 -0
  268. package/src/internal/client/dom/blocks/each.js +723 -0
  269. package/src/internal/client/dom/blocks/html.js +128 -0
  270. package/src/internal/client/dom/blocks/if.js +82 -0
  271. package/src/internal/client/dom/blocks/key.js +40 -0
  272. package/src/internal/client/dom/blocks/slot.js +44 -0
  273. package/src/internal/client/dom/blocks/snippet.js +103 -0
  274. package/src/internal/client/dom/blocks/svelte-component.js +61 -0
  275. package/src/internal/client/dom/blocks/svelte-element.js +152 -0
  276. package/src/internal/client/dom/blocks/svelte-head.js +61 -0
  277. package/src/internal/client/dom/css.js +33 -0
  278. package/src/internal/client/dom/elements/actions.js +43 -0
  279. package/src/internal/client/dom/elements/attachments.js +33 -0
  280. package/src/internal/client/dom/elements/attributes.js +657 -0
  281. package/src/internal/client/dom/elements/bindings/document.js +17 -0
  282. package/src/internal/client/dom/elements/bindings/input.js +312 -0
  283. package/src/internal/client/dom/elements/bindings/media.js +233 -0
  284. package/src/internal/client/dom/elements/bindings/navigator.js +11 -0
  285. package/src/internal/client/dom/elements/bindings/props.js +22 -0
  286. package/src/internal/client/dom/elements/bindings/select.js +159 -0
  287. package/src/internal/client/dom/elements/bindings/shared.js +76 -0
  288. package/src/internal/client/dom/elements/bindings/size.js +107 -0
  289. package/src/internal/client/dom/elements/bindings/this.js +61 -0
  290. package/src/internal/client/dom/elements/bindings/universal.js +75 -0
  291. package/src/internal/client/dom/elements/bindings/window.js +66 -0
  292. package/src/internal/client/dom/elements/class.js +51 -0
  293. package/src/internal/client/dom/elements/custom-element.js +344 -0
  294. package/src/internal/client/dom/elements/customizable-select.js +99 -0
  295. package/src/internal/client/dom/elements/events.js +355 -0
  296. package/src/internal/client/dom/elements/misc.js +58 -0
  297. package/src/internal/client/dom/elements/style.js +57 -0
  298. package/src/internal/client/dom/elements/transitions.js +471 -0
  299. package/src/internal/client/dom/hydration.js +125 -0
  300. package/src/internal/client/dom/legacy/event-modifiers.js +127 -0
  301. package/src/internal/client/dom/legacy/lifecycle.js +82 -0
  302. package/src/internal/client/dom/legacy/misc.js +68 -0
  303. package/src/internal/client/dom/operations.js +293 -0
  304. package/src/internal/client/dom/reconciler.js +25 -0
  305. package/src/internal/client/dom/task.js +42 -0
  306. package/src/internal/client/dom/template.js +401 -0
  307. package/src/internal/client/error-handling.js +118 -0
  308. package/src/internal/client/errors.js +510 -0
  309. package/src/internal/client/hydratable.js +33 -0
  310. package/src/internal/client/index.js +183 -0
  311. package/src/internal/client/legacy.js +46 -0
  312. package/src/internal/client/loop.js +48 -0
  313. package/src/internal/client/proxy.js +432 -0
  314. package/src/internal/client/reactivity/async.js +306 -0
  315. package/src/internal/client/reactivity/batch.js +1057 -0
  316. package/src/internal/client/reactivity/deriveds.js +426 -0
  317. package/src/internal/client/reactivity/effects.js +718 -0
  318. package/src/internal/client/reactivity/equality.js +31 -0
  319. package/src/internal/client/reactivity/props.js +430 -0
  320. package/src/internal/client/reactivity/sources.js +370 -0
  321. package/src/internal/client/reactivity/status.js +25 -0
  322. package/src/internal/client/reactivity/store.js +203 -0
  323. package/src/internal/client/reactivity/utils.js +40 -0
  324. package/src/internal/client/render.js +335 -0
  325. package/src/internal/client/runtime.js +827 -0
  326. package/src/internal/client/timing.js +16 -0
  327. package/src/internal/client/validate.js +54 -0
  328. package/src/internal/client/warnings.js +271 -0
  329. package/src/internal/disclose-version.js +6 -0
  330. package/src/internal/flags/async.js +3 -0
  331. package/src/internal/flags/index.js +23 -0
  332. package/src/internal/flags/legacy.js +3 -0
  333. package/src/internal/flags/tracing.js +3 -0
  334. package/src/internal/index.js +5 -0
  335. package/src/internal/server/abort-signal.js +13 -0
  336. package/src/internal/server/blocks/html.js +11 -0
  337. package/src/internal/server/blocks/snippet.js +24 -0
  338. package/src/internal/server/context.js +132 -0
  339. package/src/internal/server/crypto.js +45 -0
  340. package/src/internal/server/dev.js +115 -0
  341. package/src/internal/server/errors.js +131 -0
  342. package/src/internal/server/hydratable.js +142 -0
  343. package/src/internal/server/hydration.js +6 -0
  344. package/src/internal/server/index.js +544 -0
  345. package/src/internal/server/render-context.js +86 -0
  346. package/src/internal/server/renderer.js +923 -0
  347. package/src/internal/server/warnings.js +29 -0
  348. package/src/internal/shared/attributes.js +225 -0
  349. package/src/internal/shared/clone.js +137 -0
  350. package/src/internal/shared/dev.js +65 -0
  351. package/src/internal/shared/errors.js +134 -0
  352. package/src/internal/shared/utils.js +144 -0
  353. package/src/internal/shared/validate.js +47 -0
  354. package/src/internal/shared/warnings.js +40 -0
  355. package/src/legacy/legacy-client.js +281 -0
  356. package/src/legacy/legacy-server.js +112 -0
  357. package/src/motion/index.js +32 -0
  358. package/src/motion/spring.js +369 -0
  359. package/src/motion/tweened.js +306 -0
  360. package/src/motion/utils.js +7 -0
  361. package/src/reactivity/create-subscriber.js +95 -0
  362. package/src/reactivity/date.js +118 -0
  363. package/src/reactivity/index-client.js +7 -0
  364. package/src/reactivity/index-server.js +23 -0
  365. package/src/reactivity/map.js +273 -0
  366. package/src/reactivity/media-query.js +55 -0
  367. package/src/reactivity/reactive-value.js +24 -0
  368. package/src/reactivity/set.js +213 -0
  369. package/src/reactivity/url-search-params.js +174 -0
  370. package/src/reactivity/url.js +205 -0
  371. package/src/reactivity/window/index.js +161 -0
  372. package/src/server/index.js +1 -0
  373. package/src/store/index-client.js +169 -0
  374. package/src/store/index-server.js +101 -0
  375. package/src/store/shared/index.js +209 -0
  376. package/src/store/utils.js +36 -0
  377. package/src/transition/index.js +300 -0
  378. package/src/utils.js +504 -0
  379. package/src/version.js +8 -0
  380. package/store.d.ts +1 -0
  381. package/svelte-html.d.ts +245 -0
  382. package/transition.d.ts +1 -0
  383. package/types/compiler/interfaces.d.ts +1 -0
  384. package/types/compiler/preprocess.d.ts +1 -0
  385. package/types/index.d.ts +3744 -0
  386. package/types/index.d.ts.map +280 -0
@@ -0,0 +1,1996 @@
1
+ /** @import { VariableDeclarator, Node, Identifier, AssignmentExpression, LabeledStatement, ExpressionStatement } from 'estree' */
2
+ /** @import { Visitors } from 'zimmerframe' */
3
+ /** @import { ComponentAnalysis } from '../phases/types.js' */
4
+ /** @import { Scope } from '../phases/scope.js' */
5
+ /** @import { AST, Binding, ValidatedCompileOptions } from '#compiler' */
6
+ import MagicString from 'magic-string';
7
+ import { walk } from 'zimmerframe';
8
+ import { parse } from '../phases/1-parse/index.js';
9
+ import { regex_valid_component_name } from '../phases/1-parse/state/element.js';
10
+ import { analyze_component } from '../phases/2-analyze/index.js';
11
+ import { get_rune } from '../phases/scope.js';
12
+ import { reset, UNKNOWN_FILENAME } from '../state.js';
13
+ import {
14
+ extract_identifiers,
15
+ extract_all_identifiers_from_expression,
16
+ is_text_attribute
17
+ } from '../utils/ast.js';
18
+ import { migrate_svelte_ignore } from '../utils/extract_svelte_ignore.js';
19
+ import { validate_component_options } from '../validate-options.js';
20
+ import { is_reserved, is_svg, is_void } from '../../utils.js';
21
+ import { regex_is_valid_identifier } from '../phases/patterns.js';
22
+
23
+ const regex_style_tags = /(<style[^>]+>)([\S\s]*?)(<\/style>)/g;
24
+ const style_placeholder = '/*$$__STYLE_CONTENT__$$*/';
25
+
26
+ let has_migration_task = false;
27
+
28
+ class MigrationError extends Error {
29
+ /**
30
+ * @param {string} msg
31
+ */
32
+ constructor(msg) {
33
+ super(msg);
34
+ }
35
+ }
36
+
37
+ /**
38
+ *
39
+ * @param {State} state
40
+ */
41
+ function migrate_css(state) {
42
+ if (!state.analysis.css.ast?.start) return;
43
+ const css_contents = state.str
44
+ .snip(state.analysis.css.ast.start, /** @type {number} */ (state.analysis.css.ast?.end))
45
+ .toString();
46
+ let code = css_contents;
47
+ let starting = 0;
48
+
49
+ // since we already blank css we can't work directly on `state.str` so we will create a copy that we can update
50
+ const str = new MagicString(code);
51
+ while (code) {
52
+ if (
53
+ code.startsWith(':has') ||
54
+ code.startsWith(':is') ||
55
+ code.startsWith(':where') ||
56
+ code.startsWith(':not')
57
+ ) {
58
+ let start = code.indexOf('(') + 1;
59
+ let is_global = false;
60
+
61
+ const global_str = ':global';
62
+ const next_global = code.indexOf(global_str);
63
+ const str_between = code.substring(start, next_global);
64
+ if (!str_between.trim()) {
65
+ is_global = true;
66
+ start += global_str.length;
67
+ } else {
68
+ const prev_global = css_contents.lastIndexOf(global_str, starting);
69
+ if (prev_global > -1) {
70
+ const end =
71
+ find_closing_parenthesis(css_contents.indexOf('(', prev_global) + 1, css_contents) -
72
+ starting;
73
+ if (end > start) {
74
+ starting += end;
75
+ code = code.substring(end);
76
+ continue;
77
+ }
78
+ }
79
+ }
80
+
81
+ const end = find_closing_parenthesis(start, code);
82
+ if (start && end) {
83
+ if (!is_global && !code.startsWith(':not')) {
84
+ str.prependLeft(starting + start, ':global(');
85
+ str.appendRight(starting + end - 1, ')');
86
+ }
87
+ starting += end - 1;
88
+ code = code.substring(end - 1);
89
+ continue;
90
+ }
91
+ }
92
+ starting++;
93
+ code = code.substring(1);
94
+ }
95
+ state.str.update(state.analysis.css.ast?.start, state.analysis.css.ast?.end, str.toString());
96
+ }
97
+
98
+ /**
99
+ * @param {number} start
100
+ * @param {string} code
101
+ */
102
+ function find_closing_parenthesis(start, code) {
103
+ let parenthesis = 1;
104
+ let end = start;
105
+ let char = code[end];
106
+ // find the closing parenthesis
107
+ while (parenthesis !== 0 && char) {
108
+ if (char === '(') parenthesis++;
109
+ if (char === ')') parenthesis--;
110
+ end++;
111
+ char = code[end];
112
+ }
113
+ return end;
114
+ }
115
+
116
+ /**
117
+ * Does a best-effort migration of Svelte code towards using runes, event attributes and render tags.
118
+ * May throw an error if the code is too complex to migrate automatically.
119
+ *
120
+ * @param {string} source
121
+ * @param {{ filename?: string, use_ts?: boolean }} [options]
122
+ * @returns {{ code: string; }}
123
+ */
124
+ export function migrate(source, { filename, use_ts } = {}) {
125
+ let og_source = source;
126
+ try {
127
+ has_migration_task = false;
128
+ // Blank CSS, could contain SCSS or similar that needs a preprocessor.
129
+ // Since we don't care about CSS in this migration, we'll just ignore it.
130
+ /** @type {Array<[number, string]>} */
131
+ const style_contents = [];
132
+ source = source.replace(regex_style_tags, (_, start, content, end, idx) => {
133
+ style_contents.push([idx + start.length, content]);
134
+ return start + style_placeholder + end;
135
+ });
136
+
137
+ reset({ warning: () => false, filename });
138
+
139
+ let parsed = parse(source);
140
+
141
+ const { customElement: customElementOptions, ...parsed_options } = parsed.options || {};
142
+
143
+ /** @type {ValidatedCompileOptions} */
144
+ const combined_options = {
145
+ ...validate_component_options({}, ''),
146
+ ...parsed_options,
147
+ customElementOptions,
148
+ filename: filename ?? UNKNOWN_FILENAME,
149
+ experimental: {
150
+ async: true
151
+ }
152
+ };
153
+
154
+ const str = new MagicString(source);
155
+ const analysis = analyze_component(parsed, source, combined_options);
156
+ const indent = guess_indent(source);
157
+
158
+ str.replaceAll(/(<svelte:options\s.*?\s?)accessors\s?/g, (_, $1) => $1);
159
+
160
+ for (const content of style_contents) {
161
+ str.overwrite(content[0], content[0] + style_placeholder.length, content[1]);
162
+ }
163
+
164
+ /** @type {State} */
165
+ let state = {
166
+ scope: analysis.instance.scope,
167
+ analysis,
168
+ filename,
169
+ str,
170
+ indent,
171
+ props: [],
172
+ props_insertion_point: parsed.instance?.content.start ?? 0,
173
+ has_props_rune: false,
174
+ has_type_or_fallback: false,
175
+ end: source.length,
176
+ names: {
177
+ props: analysis.root.unique('props').name,
178
+ rest: analysis.root.unique('rest').name,
179
+
180
+ // event stuff
181
+ run: analysis.root.unique('run').name,
182
+ handlers: analysis.root.unique('handlers').name,
183
+ stopImmediatePropagation: analysis.root.unique('stopImmediatePropagation').name,
184
+ preventDefault: analysis.root.unique('preventDefault').name,
185
+ stopPropagation: analysis.root.unique('stopPropagation').name,
186
+ once: analysis.root.unique('once').name,
187
+ self: analysis.root.unique('self').name,
188
+ trusted: analysis.root.unique('trusted').name,
189
+ createBubbler: analysis.root.unique('createBubbler').name,
190
+ bubble: analysis.root.unique('bubble').name,
191
+ passive: analysis.root.unique('passive').name,
192
+ nonpassive: analysis.root.unique('nonpassive').name
193
+ },
194
+ legacy_imports: new Set(),
195
+ script_insertions: new Set(),
196
+ derived_components: new Map(),
197
+ derived_conflicting_slots: new Map(),
198
+ derived_labeled_statements: new Set(),
199
+ has_svelte_self: false,
200
+ uses_ts:
201
+ // Some people could use jsdoc but have a tsconfig.json, so double-check file for jsdoc indicators
202
+ (use_ts && !source.includes('@type {')) ||
203
+ !!parsed.instance?.attributes.some(
204
+ (attr) => attr.name === 'lang' && /** @type {any} */ (attr).value[0].data === 'ts'
205
+ )
206
+ };
207
+
208
+ if (parsed.module) {
209
+ const context = parsed.module.attributes.find((attr) => attr.name === 'context');
210
+ if (context) {
211
+ state.str.update(context.start, context.end, 'module');
212
+ }
213
+ }
214
+
215
+ if (parsed.instance) {
216
+ walk(parsed.instance.content, state, instance_script);
217
+ }
218
+
219
+ state = { ...state, scope: analysis.template.scope };
220
+ walk(parsed.fragment, state, template);
221
+
222
+ let insertion_point = parsed.instance
223
+ ? /** @type {number} */ (parsed.instance.content.start)
224
+ : 0;
225
+
226
+ const need_script =
227
+ state.legacy_imports.size > 0 ||
228
+ state.derived_components.size > 0 ||
229
+ state.derived_conflicting_slots.size > 0 ||
230
+ state.script_insertions.size > 0 ||
231
+ state.props.length > 0 ||
232
+ analysis.uses_rest_props ||
233
+ analysis.uses_props ||
234
+ state.has_svelte_self;
235
+
236
+ const need_ts_tag =
237
+ state.uses_ts &&
238
+ (!parsed.instance || !parsed.instance.attributes.some((attr) => attr.name === 'lang'));
239
+
240
+ if (!parsed.instance && need_script) {
241
+ str.appendRight(0, need_ts_tag ? '<script lang="ts">' : '<script>');
242
+ }
243
+
244
+ if (state.has_svelte_self && filename) {
245
+ const file = filename.split('/').pop();
246
+ str.appendRight(
247
+ insertion_point,
248
+ `\n${indent}import ${state.analysis.name} from './${file}';`
249
+ );
250
+ }
251
+
252
+ const specifiers = [...state.legacy_imports].map((imported) => {
253
+ const local = state.names[imported];
254
+ return imported === local ? imported : `${imported} as ${local}`;
255
+ });
256
+
257
+ const legacy_import = `import { ${specifiers.join(', ')} } from 'svelte/legacy';\n`;
258
+
259
+ if (state.legacy_imports.size > 0) {
260
+ str.appendRight(insertion_point, `\n${indent}${legacy_import}`);
261
+ }
262
+
263
+ if (state.script_insertions.size > 0) {
264
+ str.appendRight(
265
+ insertion_point,
266
+ `\n${indent}${[...state.script_insertions].join(`\n${indent}`)}`
267
+ );
268
+ }
269
+
270
+ insertion_point = state.props_insertion_point;
271
+
272
+ /**
273
+ * @param {"derived"|"props"|"bindable"} rune
274
+ */
275
+ function check_rune_binding(rune) {
276
+ const has_rune_binding = !!state.scope.get(rune);
277
+ if (has_rune_binding) {
278
+ throw new MigrationError(
279
+ `migrating this component would require adding a \`$${rune}\` rune but there's already a variable named ${rune}.\n Rename the variable and try again or migrate by hand.`
280
+ );
281
+ }
282
+ }
283
+
284
+ if (state.props.length > 0 || analysis.uses_rest_props || analysis.uses_props) {
285
+ const has_many_props = state.props.length > 3;
286
+ const newline_separator = `\n${indent}${indent}`;
287
+ const props_separator = has_many_props ? newline_separator : ' ';
288
+ let props = '';
289
+ if (analysis.uses_props) {
290
+ props = `...${state.names.props}`;
291
+ } else {
292
+ props = state.props
293
+ .filter((prop) => !prop.type_only)
294
+ .map((prop) => {
295
+ let prop_str =
296
+ prop.local === prop.exported ? prop.local : `${prop.exported}: ${prop.local}`;
297
+ if (prop.bindable) {
298
+ check_rune_binding('bindable');
299
+ prop_str += ` = $bindable(${prop.init})`;
300
+ } else if (prop.init) {
301
+ prop_str += ` = ${prop.init}`;
302
+ }
303
+ return prop_str;
304
+ })
305
+ .join(`,${props_separator}`);
306
+
307
+ if (analysis.uses_rest_props) {
308
+ props += `${state.props.length > 0 ? `,${props_separator}` : ''}...${state.names.rest}`;
309
+ }
310
+ }
311
+
312
+ if (state.has_props_rune) {
313
+ // some render tags or forwarded event attributes to add
314
+ str.appendRight(insertion_point, ` ${props},`);
315
+ } else {
316
+ const type_name = state.scope.root.unique('Props').name;
317
+ let type = '';
318
+
319
+ // Try to infer when we don't want to add types (e.g. user doesn't use types, or this is a zero-types +page.svelte)
320
+ if (state.has_type_or_fallback || state.props.every((prop) => prop.slot_name)) {
321
+ if (state.uses_ts) {
322
+ type = `interface ${type_name} {${newline_separator}${state.props
323
+ .map((prop) => {
324
+ const comment = prop.comment ? `${prop.comment}${newline_separator}` : '';
325
+ return `${comment}${prop.exported}${prop.optional ? '?' : ''}: ${prop.type};${prop.trailing_comment ? ' ' + prop.trailing_comment : ''}`;
326
+ })
327
+ .join(newline_separator)}`;
328
+ if (analysis.uses_props || analysis.uses_rest_props) {
329
+ type += `${state.props.length > 0 ? newline_separator : ''}[key: string]: any`;
330
+ }
331
+ type += `\n${indent}}`;
332
+ } else {
333
+ type = `/**\n${indent} * @typedef {Object} ${type_name}${state.props
334
+ .map((prop) => {
335
+ return `\n${indent} * @property {${prop.type}} ${prop.optional ? `[${prop.exported}]` : prop.exported}${prop.comment ? ` - ${prop.comment}` : ''}${prop.trailing_comment ? ` - ${prop.trailing_comment.trim()}` : ''}`;
336
+ })
337
+ .join(``)}\n${indent} */`;
338
+ }
339
+ }
340
+
341
+ let props_declaration = `let {${props_separator}${props}${has_many_props ? `\n${indent}` : ' '}}`;
342
+ if (state.uses_ts) {
343
+ if (type) {
344
+ props_declaration = `${type}\n\n${indent}${props_declaration}`;
345
+ }
346
+ check_rune_binding('props');
347
+ props_declaration = `${props_declaration}${type ? `: ${type_name}` : ''} = $props();`;
348
+ } else {
349
+ if (type) {
350
+ props_declaration = `${state.props.length > 0 ? `${type}\n\n${indent}` : ''}/** @type {${state.props.length > 0 ? type_name : ''}${analysis.uses_props || analysis.uses_rest_props ? `${state.props.length > 0 ? ' & ' : ''}{ [key: string]: any }` : ''}} */\n${indent}${props_declaration}`;
351
+ }
352
+ check_rune_binding('props');
353
+ props_declaration = `${props_declaration} = $props();`;
354
+ }
355
+
356
+ props_declaration = `\n${indent}${props_declaration}`;
357
+ str.appendRight(insertion_point, props_declaration);
358
+ }
359
+
360
+ if (parsed.instance && need_ts_tag) {
361
+ str.appendRight(parsed.instance.start + '<script'.length, ' lang="ts"');
362
+ }
363
+ }
364
+
365
+ /**
366
+ * If true, then we need to move all reactive statements to the end of the script block,
367
+ * in their correct order. Svelte 4 reordered reactive statements, $derived/$effect.pre
368
+ * don't have this behavior.
369
+ */
370
+ let needs_reordering = false;
371
+
372
+ for (const [node, { dependencies }] of state.analysis.reactive_statements) {
373
+ /** @type {Binding[]} */
374
+ let ids = [];
375
+ if (
376
+ node.body.type === 'ExpressionStatement' &&
377
+ node.body.expression.type === 'AssignmentExpression'
378
+ ) {
379
+ ids = extract_identifiers(node.body.expression.left)
380
+ .map((id) => state.scope.get(id.name))
381
+ .filter((id) => !!id);
382
+ }
383
+
384
+ if (
385
+ dependencies.some(
386
+ (dep) =>
387
+ !ids.includes(dep) &&
388
+ (dep.kind === 'prop' || dep.kind === 'bindable_prop'
389
+ ? state.props_insertion_point
390
+ : /** @type {number} */ (dep.node.start)) > /** @type {number} */ (node.start)
391
+ )
392
+ ) {
393
+ needs_reordering = true;
394
+ break;
395
+ }
396
+ }
397
+
398
+ if (needs_reordering) {
399
+ const nodes = Array.from(state.analysis.reactive_statements.keys());
400
+ for (const node of nodes) {
401
+ const { start, end } = get_node_range(source, node);
402
+ str.appendLeft(end, '\n');
403
+ str.move(start, end, /** @type {number} */ (parsed.instance?.content.end));
404
+ str.update(start - (source[start - 2] === '\r' ? 2 : 1), start, '');
405
+ }
406
+ }
407
+
408
+ insertion_point = parsed.instance
409
+ ? /** @type {number} */ (parsed.instance.content.end)
410
+ : insertion_point;
411
+
412
+ if (state.derived_components.size > 0) {
413
+ check_rune_binding('derived');
414
+ str.appendRight(
415
+ insertion_point,
416
+ `\n${indent}${[...state.derived_components.entries()].map(([init, name]) => `const ${name} = $derived(${init});`).join(`\n${indent}`)}\n`
417
+ );
418
+ }
419
+
420
+ if (state.derived_conflicting_slots.size > 0) {
421
+ check_rune_binding('derived');
422
+ str.appendRight(
423
+ insertion_point,
424
+ `\n${indent}${[...state.derived_conflicting_slots.entries()].map(([name, init]) => `const ${name} = $derived(${init});`).join(`\n${indent}`)}\n`
425
+ );
426
+ }
427
+
428
+ if (state.props.length > 0 && state.analysis.accessors) {
429
+ str.appendRight(
430
+ insertion_point,
431
+ `\n${indent}export {${state.props.reduce((acc, prop) => (prop.slot_name || prop.type_only ? acc : `${acc}\n${indent}\t${prop.local},`), '')}\n${indent}}\n`
432
+ );
433
+ }
434
+
435
+ if (!parsed.instance && need_script) {
436
+ str.appendRight(insertion_point, '\n</script>\n\n');
437
+ }
438
+ migrate_css(state);
439
+ return {
440
+ code: str.toString()
441
+ };
442
+ } catch (e) {
443
+ if (!(e instanceof MigrationError)) {
444
+ // eslint-disable-next-line no-console
445
+ console.error('Error while migrating Svelte code', e);
446
+ }
447
+ has_migration_task = true;
448
+ return {
449
+ code: `<!-- @migration-task Error while migrating Svelte code: ${/** @type {any} */ (e).message} -->\n${og_source}`
450
+ };
451
+ } finally {
452
+ if (has_migration_task) {
453
+ // eslint-disable-next-line no-console
454
+ console.log(
455
+ `One or more \`@migration-task\` comments were added to ${filename ? `\`${filename}\`` : "a file (unfortunately we don't know the name)"}, please check them and complete the migration manually.`
456
+ );
457
+ }
458
+ }
459
+ }
460
+
461
+ /**
462
+ * @typedef {{
463
+ * scope: Scope;
464
+ * str: MagicString;
465
+ * analysis: ComponentAnalysis;
466
+ * filename?: string;
467
+ * indent: string;
468
+ * props: Array<{ local: string; exported: string; init: string; bindable: boolean; slot_name?: string; optional: boolean; type: string; comment?: string; trailing_comment?: string; type_only?: boolean; needs_refine_type?: boolean; }>;
469
+ * props_insertion_point: number;
470
+ * has_props_rune: boolean;
471
+ * has_type_or_fallback: boolean;
472
+ * end: number;
473
+ * names: Record<string, string>;
474
+ * legacy_imports: Set<string>;
475
+ * script_insertions: Set<string>;
476
+ * derived_components: Map<string, string>;
477
+ * derived_conflicting_slots: Map<string, string>;
478
+ * derived_labeled_statements: Set<LabeledStatement>;
479
+ * has_svelte_self: boolean;
480
+ * uses_ts: boolean;
481
+ * }} State
482
+ */
483
+
484
+ /** @type {Visitors<AST.SvelteNode, State>} */
485
+ const instance_script = {
486
+ _(node, { state, next }) {
487
+ // @ts-expect-error
488
+ const comments = node.leadingComments;
489
+ if (comments) {
490
+ for (const comment of comments) {
491
+ if (comment.type === 'Line') {
492
+ const migrated = migrate_svelte_ignore(comment.value);
493
+ if (migrated !== comment.value) {
494
+ state.str.overwrite(comment.start + '//'.length, comment.end, migrated);
495
+ }
496
+ }
497
+ }
498
+ }
499
+ next();
500
+ },
501
+ Identifier(node, { state, path }) {
502
+ handle_identifier(node, state, path);
503
+ },
504
+ ImportDeclaration(node, { state }) {
505
+ state.props_insertion_point = node.end ?? state.props_insertion_point;
506
+ if (node.source.value === 'svelte') {
507
+ let illegal_specifiers = [];
508
+ let removed_specifiers = 0;
509
+ for (let specifier of node.specifiers) {
510
+ if (
511
+ specifier.type === 'ImportSpecifier' &&
512
+ specifier.imported.type === 'Identifier' &&
513
+ ['beforeUpdate', 'afterUpdate'].includes(specifier.imported.name)
514
+ ) {
515
+ const references = state.scope.references.get(specifier.local.name);
516
+ if (!references) {
517
+ let end = /** @type {number} */ (
518
+ state.str.original.indexOf(',', specifier.end) !== -1 &&
519
+ state.str.original.indexOf(',', specifier.end) <
520
+ state.str.original.indexOf('}', specifier.end)
521
+ ? state.str.original.indexOf(',', specifier.end) + 1
522
+ : specifier.end
523
+ );
524
+ while (state.str.original[end].trim() === '') end++;
525
+ state.str.remove(/** @type {number} */ (specifier.start), end);
526
+ removed_specifiers++;
527
+ continue;
528
+ }
529
+ illegal_specifiers.push(specifier.imported.name);
530
+ }
531
+ }
532
+ if (removed_specifiers === node.specifiers.length) {
533
+ state.str.remove(/** @type {number} */ (node.start), /** @type {number} */ (node.end));
534
+ }
535
+ if (illegal_specifiers.length > 0) {
536
+ throw new MigrationError(
537
+ `Can't migrate code with ${illegal_specifiers.join(' and ')}. Please migrate by hand.`
538
+ );
539
+ }
540
+ }
541
+ },
542
+ ExportNamedDeclaration(node, { state, next }) {
543
+ if (node.declaration) {
544
+ next();
545
+ return;
546
+ }
547
+
548
+ let count_removed = 0;
549
+ for (const specifier of node.specifiers) {
550
+ if (specifier.local.type !== 'Identifier') continue;
551
+
552
+ const binding = state.scope.get(specifier.local.name);
553
+ if (binding?.kind === 'bindable_prop') {
554
+ state.str.remove(
555
+ /** @type {number} */ (specifier.start),
556
+ /** @type {number} */ (specifier.end)
557
+ );
558
+ count_removed++;
559
+ }
560
+ }
561
+ if (count_removed === node.specifiers.length) {
562
+ state.str.remove(/** @type {number} */ (node.start), /** @type {number} */ (node.end));
563
+ }
564
+ },
565
+ VariableDeclaration(node, { state, path, visit, next }) {
566
+ if (state.scope !== state.analysis.instance.scope) {
567
+ return;
568
+ }
569
+
570
+ let nr_of_props = 0;
571
+
572
+ for (let i = 0; i < node.declarations.length; i++) {
573
+ const declarator = node.declarations[i];
574
+ if (state.analysis.runes) {
575
+ if (get_rune(declarator.init, state.scope) === '$props') {
576
+ state.props_insertion_point = /** @type {number} */ (declarator.id.start) + 1;
577
+ state.has_props_rune = true;
578
+ }
579
+ continue;
580
+ }
581
+
582
+ let bindings;
583
+ try {
584
+ bindings = state.scope.get_bindings(declarator);
585
+ } catch (e) {
586
+ // no bindings, so we can skip this
587
+ next();
588
+ continue;
589
+ }
590
+ const has_state = bindings.some((binding) => binding.kind === 'state');
591
+ const has_props = bindings.some((binding) => binding.kind === 'bindable_prop');
592
+
593
+ if (!has_state && !has_props) {
594
+ next();
595
+ continue;
596
+ }
597
+
598
+ if (has_props) {
599
+ nr_of_props++;
600
+
601
+ if (declarator.id.type !== 'Identifier') {
602
+ // TODO invest time in this?
603
+ throw new MigrationError(
604
+ 'Encountered an export declaration pattern that is not supported for automigration.'
605
+ );
606
+ // Turn export let into props. It's really really weird because export let { x: foo, z: [bar]} = ..
607
+ // means that foo and bar are the props (i.e. the leaves are the prop names), not x and z.
608
+ // const tmp = b.id(state.scope.generate('tmp'));
609
+ // const paths = extract_paths(declarator.id, tmp);
610
+ // state.props_pre.push(
611
+ // b.declaration('const', tmp, visit(declarator.init!) as Expression)
612
+ // );
613
+ // for (const path of paths) {
614
+ // const name = (path.node as Identifier).name;
615
+ // const binding = state.scope.get(name)!;
616
+ // const value = path.expression;
617
+ // if (binding.kind === 'bindable_prop' || binding.kind === 'rest_prop') {
618
+ // state.props.push({
619
+ // local: name,
620
+ // exported: binding.prop_alias ? binding.prop_alias : name,
621
+ // init: value
622
+ // });
623
+ // state.props_insertion_point = /** @type {number} */(declarator.end);
624
+ // } else {
625
+ // declarations.push(b.declarator(path.node, value));
626
+ // }
627
+ // }
628
+ }
629
+
630
+ const name = declarator.id.name;
631
+ const binding = /** @type {Binding} */ (state.scope.get(name));
632
+
633
+ if (state.analysis.uses_props && (declarator.init || binding.updated)) {
634
+ throw new MigrationError(
635
+ '$$props is used together with named props in a way that cannot be automatically migrated.'
636
+ );
637
+ }
638
+
639
+ const prop = state.props.find((prop) => prop.exported === (binding.prop_alias || name));
640
+ if (prop) {
641
+ next();
642
+ // $$Props type was used
643
+ prop.init = declarator.init
644
+ ? state.str
645
+ .snip(
646
+ /** @type {number} */ (declarator.init.start),
647
+ /** @type {number} */ (declarator.init.end)
648
+ )
649
+ .toString()
650
+ : '';
651
+ prop.bindable = binding.updated;
652
+ prop.exported = binding.prop_alias || name;
653
+ prop.type_only = false;
654
+ } else {
655
+ next();
656
+ state.props.push({
657
+ local: name,
658
+ exported: binding.prop_alias ? binding.prop_alias : name,
659
+ init: declarator.init
660
+ ? state.str
661
+ .snip(
662
+ /** @type {number} */ (declarator.init.start),
663
+ /** @type {number} */ (declarator.init.end)
664
+ )
665
+ .toString()
666
+ : '',
667
+ optional: !!declarator.init,
668
+ bindable: binding.updated,
669
+ ...extract_type_and_comment(declarator, state, path)
670
+ });
671
+ }
672
+
673
+ let start = /** @type {number} */ (declarator.start);
674
+ let end = /** @type {number} */ (declarator.end);
675
+
676
+ // handle cases like let a,b,c; where only some are exported
677
+ if (node.declarations.length > 1) {
678
+ // move the insertion point after the node itself;
679
+ state.props_insertion_point = /** @type {number} */ (node.end);
680
+ // if it's not the first declaration remove from the , of the previous declaration
681
+ if (i !== 0) {
682
+ start = state.str.original.indexOf(
683
+ ',',
684
+ /** @type {number} */ (node.declarations[i - 1].end)
685
+ );
686
+ }
687
+ // if it's not the last declaration remove either from up until the
688
+ // start of the next declaration (if it's the first declaration) or
689
+ // up until the last index of , from the next declaration
690
+ if (i !== node.declarations.length - 1) {
691
+ if (i === 0) {
692
+ end = /** @type {number} */ (node.declarations[i + 1].start);
693
+ } else {
694
+ end = state.str.original.lastIndexOf(
695
+ ',',
696
+ /** @type {number} */ (node.declarations[i + 1].start)
697
+ );
698
+ }
699
+ }
700
+ } else {
701
+ state.props_insertion_point = /** @type {number} */ (declarator.end);
702
+ }
703
+
704
+ state.str.update(start, end, '');
705
+
706
+ continue;
707
+ }
708
+
709
+ /**
710
+ * @param {"state"|"derived"} rune
711
+ */
712
+ function check_rune_binding(rune) {
713
+ const has_rune_binding = !!state.scope.get(rune);
714
+ if (has_rune_binding) {
715
+ throw new MigrationError(
716
+ `can't migrate \`${state.str.original.substring(/** @type {number} */ (node.start), node.end)}\` to \`$${rune}\` because there's a variable named ${rune}.\n Rename the variable and try again or migrate by hand.`
717
+ );
718
+ }
719
+ }
720
+
721
+ // state
722
+ if (declarator.init) {
723
+ let { start, end } = /** @type {{ start: number, end: number }} */ (declarator.init);
724
+
725
+ if (declarator.init.type === 'SequenceExpression') {
726
+ while (state.str.original[start] !== '(') start -= 1;
727
+ while (state.str.original[end - 1] !== ')') end += 1;
728
+ }
729
+
730
+ check_rune_binding('state');
731
+
732
+ state.str.prependLeft(start, '$state(');
733
+ state.str.appendRight(end, ')');
734
+ } else {
735
+ /**
736
+ * @type {AssignmentExpression | undefined}
737
+ */
738
+ let assignment_in_labeled;
739
+ /**
740
+ * @type {LabeledStatement | undefined}
741
+ */
742
+ let labeled_statement;
743
+
744
+ // Analyze declaration bindings to see if they're exclusively updated within a single reactive statement
745
+ const possible_derived = bindings.every((binding) =>
746
+ binding.references.every((reference) => {
747
+ const declaration = reference.path.find((el) => el.type === 'VariableDeclaration');
748
+ const assignment = reference.path.find((el) => el.type === 'AssignmentExpression');
749
+ const update = reference.path.find((el) => el.type === 'UpdateExpression');
750
+ const labeled = /** @type {LabeledStatement | undefined} */ (
751
+ reference.path.find((el) => el.type === 'LabeledStatement' && el.label.name === '$')
752
+ );
753
+
754
+ if (
755
+ assignment &&
756
+ labeled &&
757
+ // ensure that $: foo = bar * 2 is not counted as a reassignment of bar
758
+ (labeled.body.type !== 'ExpressionStatement' ||
759
+ labeled.body.expression !== assignment ||
760
+ (assignment.left.type === 'Identifier' &&
761
+ assignment.left.name === binding.node.name))
762
+ ) {
763
+ if (assignment_in_labeled) return false;
764
+ assignment_in_labeled = /** @type {AssignmentExpression} */ (assignment);
765
+ labeled_statement = labeled;
766
+ }
767
+
768
+ return (
769
+ !update &&
770
+ ((declaration && binding.initial) ||
771
+ (labeled && assignment) ||
772
+ (!labeled && !assignment))
773
+ );
774
+ })
775
+ );
776
+
777
+ const labeled_has_single_assignment =
778
+ labeled_statement?.body.type === 'BlockStatement' &&
779
+ labeled_statement.body.body.length === 1 &&
780
+ labeled_statement.body.body[0].type === 'ExpressionStatement';
781
+
782
+ const is_expression_assignment =
783
+ labeled_statement?.body.type === 'ExpressionStatement' &&
784
+ labeled_statement.body.expression.type === 'AssignmentExpression';
785
+
786
+ let should_be_state = false;
787
+
788
+ if (is_expression_assignment) {
789
+ const body = /**@type {ExpressionStatement}*/ (labeled_statement?.body);
790
+ const expression = /**@type {AssignmentExpression}*/ (body.expression);
791
+ const [, ids] = extract_all_identifiers_from_expression(expression.right);
792
+ if (ids.length === 0) {
793
+ should_be_state = true;
794
+ state.derived_labeled_statements.add(
795
+ /** @type {LabeledStatement} */ (labeled_statement)
796
+ );
797
+ }
798
+ }
799
+
800
+ if (
801
+ !should_be_state &&
802
+ possible_derived &&
803
+ assignment_in_labeled &&
804
+ labeled_statement &&
805
+ (labeled_has_single_assignment || is_expression_assignment)
806
+ ) {
807
+ const indent = state.str.original.substring(
808
+ state.str.original.lastIndexOf('\n', /** @type {number} */ (node.start)) + 1,
809
+ /** @type {number} */ (node.start)
810
+ );
811
+ // transfer all the leading comments
812
+ if (
813
+ labeled_statement.body.type === 'BlockStatement' &&
814
+ labeled_statement.body.body[0].leadingComments
815
+ ) {
816
+ for (let comment of labeled_statement.body.body[0].leadingComments) {
817
+ state.str.prependLeft(
818
+ /** @type {number} */ (node.start),
819
+ comment.type === 'Block'
820
+ ? `/*${comment.value}*/\n${indent}`
821
+ : `// ${comment.value}\n${indent}`
822
+ );
823
+ }
824
+ }
825
+
826
+ check_rune_binding('derived');
827
+
828
+ // Someone wrote a `$: { ... }` statement which we can turn into a `$derived`
829
+ state.str.appendRight(
830
+ /** @type {number} */ (declarator.id.typeAnnotation?.end ?? declarator.id.end),
831
+ ' = $derived('
832
+ );
833
+ visit(assignment_in_labeled.right);
834
+ state.str.appendRight(
835
+ /** @type {number} */ (declarator.id.typeAnnotation?.end ?? declarator.id.end),
836
+ state.str
837
+ .snip(
838
+ /** @type {number} */ (assignment_in_labeled.right.start),
839
+ /** @type {number} */ (assignment_in_labeled.right.end)
840
+ )
841
+ .toString()
842
+ );
843
+ state.str.remove(
844
+ /** @type {number} */ (labeled_statement.start),
845
+ /** @type {number} */ (labeled_statement.end)
846
+ );
847
+ state.str.appendRight(
848
+ /** @type {number} */ (declarator.id.typeAnnotation?.end ?? declarator.id.end),
849
+ ')'
850
+ );
851
+ state.derived_labeled_statements.add(labeled_statement);
852
+
853
+ // transfer all the trailing comments
854
+ if (
855
+ labeled_statement.body.type === 'BlockStatement' &&
856
+ labeled_statement.body.body[0].trailingComments
857
+ ) {
858
+ for (let comment of labeled_statement.body.body[0].trailingComments) {
859
+ state.str.appendRight(
860
+ /** @type {number} */ (declarator.id.typeAnnotation?.end ?? declarator.id.end),
861
+ comment.type === 'Block'
862
+ ? `\n${indent}/*${comment.value}*/`
863
+ : `\n${indent}// ${comment.value}`
864
+ );
865
+ }
866
+ }
867
+ } else {
868
+ check_rune_binding('state');
869
+
870
+ state.str.prependLeft(
871
+ /** @type {number} */ (declarator.id.typeAnnotation?.end ?? declarator.id.end),
872
+ ' = $state('
873
+ );
874
+ if (should_be_state) {
875
+ // someone wrote a `$: foo = ...` statement which we can turn into `let foo = $state(...)`
876
+ state.str.appendRight(
877
+ /** @type {number} */ (declarator.id.typeAnnotation?.end ?? declarator.id.end),
878
+ state.str
879
+ .snip(
880
+ /** @type {number} */ (
881
+ /** @type {AssignmentExpression} */ (assignment_in_labeled).right.start
882
+ ),
883
+ /** @type {number} */ (
884
+ /** @type {AssignmentExpression} */ (assignment_in_labeled).right.end
885
+ )
886
+ )
887
+ .toString()
888
+ );
889
+ state.str.remove(
890
+ /** @type {number} */ (/** @type {LabeledStatement} */ (labeled_statement).start),
891
+ /** @type {number} */ (/** @type {LabeledStatement} */ (labeled_statement).end)
892
+ );
893
+ }
894
+ state.str.appendRight(
895
+ /** @type {number} */ (declarator.id.typeAnnotation?.end ?? declarator.id.end),
896
+ ')'
897
+ );
898
+ }
899
+ }
900
+ }
901
+
902
+ if (nr_of_props === node.declarations.length) {
903
+ let start = /** @type {number} */ (node.start);
904
+ let end = /** @type {number} */ (node.end);
905
+
906
+ const parent = path.at(-1);
907
+ if (parent?.type === 'ExportNamedDeclaration') {
908
+ start = /** @type {number} */ (parent.start);
909
+ end = /** @type {number} */ (parent.end);
910
+ }
911
+ while (state.str.original[start] !== '\n') start--;
912
+ while (state.str.original[end] !== '\n') end++;
913
+ state.str.update(start, end, '');
914
+ }
915
+ },
916
+ BreakStatement(node, { state, path }) {
917
+ if (path[1].type !== 'LabeledStatement') return;
918
+ if (node.label?.name !== '$') return;
919
+ state.str.update(
920
+ /** @type {number} */ (node.start),
921
+ /** @type {number} */ (node.end),
922
+ 'return;'
923
+ );
924
+ },
925
+ LabeledStatement(node, { path, state, next }) {
926
+ if (state.analysis.runes) return;
927
+ if (path.length > 1) return;
928
+ if (node.label.name !== '$') return;
929
+ if (state.derived_labeled_statements.has(node)) return;
930
+
931
+ next();
932
+
933
+ /**
934
+ * @param {"state"|"derived"} rune
935
+ */
936
+ function check_rune_binding(rune) {
937
+ const has_rune_binding = state.scope.get(rune);
938
+ if (has_rune_binding) {
939
+ throw new MigrationError(
940
+ `can't migrate \`$: ${state.str.original.substring(/** @type {number} */ (node.body.start), node.body.end)}\` to \`$${rune}\` because there's a variable named ${rune}.\n Rename the variable and try again or migrate by hand.`
941
+ );
942
+ }
943
+ }
944
+
945
+ if (
946
+ node.body.type === 'ExpressionStatement' &&
947
+ node.body.expression.type === 'AssignmentExpression'
948
+ ) {
949
+ const { left, right } = node.body.expression;
950
+
951
+ const ids = extract_identifiers(left);
952
+ const [, expression_ids] = extract_all_identifiers_from_expression(right);
953
+ const bindings = ids.map((id) => /** @type {Binding} */ (state.scope.get(id.name)));
954
+
955
+ if (bindings.every((b) => b.kind === 'legacy_reactive')) {
956
+ if (
957
+ right.type !== 'Literal' &&
958
+ bindings.every((b) => b.kind !== 'store_sub') &&
959
+ left.type !== 'MemberExpression'
960
+ ) {
961
+ let { start, end } = /** @type {{ start: number, end: number }} */ (right);
962
+
963
+ check_rune_binding('derived');
964
+
965
+ // $derived
966
+ state.str.update(
967
+ /** @type {number} */ (node.start),
968
+ /** @type {number} */ (node.body.expression.start),
969
+ 'let '
970
+ );
971
+
972
+ if (right.type === 'SequenceExpression') {
973
+ while (state.str.original[start] !== '(') start -= 1;
974
+ while (state.str.original[end - 1] !== ')') end += 1;
975
+ }
976
+
977
+ state.str.prependRight(start, `$derived(`);
978
+
979
+ // in a case like `$: ({ a } = b())`, there's already a trailing parenthesis.
980
+ // otherwise, we need to add one
981
+ if (state.str.original[/** @type {number} */ (node.body.start)] !== '(') {
982
+ state.str.appendLeft(end, `)`);
983
+ }
984
+
985
+ return;
986
+ }
987
+
988
+ for (const binding of bindings) {
989
+ if (binding.reassigned && (ids.includes(binding.node) || expression_ids.length === 0)) {
990
+ check_rune_binding('state');
991
+ const init =
992
+ binding.kind === 'state'
993
+ ? ' = $state()'
994
+ : expression_ids.length === 0
995
+ ? ` = $state(${state.str.original.substring(/** @type {number} */ (right.start), right.end)})`
996
+ : '';
997
+ // implicitly-declared variable which we need to make explicit
998
+ state.str.prependLeft(
999
+ /** @type {number} */ (node.start),
1000
+ `let ${binding.node.name}${init};\n${state.indent}`
1001
+ );
1002
+ }
1003
+ }
1004
+
1005
+ if (expression_ids.length === 0 && bindings.every((b) => b.kind !== 'store_sub')) {
1006
+ state.str.remove(/** @type {number} */ (node.start), /** @type {number} */ (node.end));
1007
+ return;
1008
+ }
1009
+ }
1010
+ }
1011
+
1012
+ state.legacy_imports.add('run');
1013
+ const is_block_stmt = node.body.type === 'BlockStatement';
1014
+ const start_end = /** @type {number} */ (node.body.start);
1015
+ // TODO try to find out if we can use $derived.by instead?
1016
+ if (is_block_stmt) {
1017
+ state.str.update(
1018
+ /** @type {number} */ (node.start),
1019
+ start_end + 1,
1020
+ `${state.names.run}(() => {`
1021
+ );
1022
+ const end = /** @type {number} */ (node.body.end);
1023
+ state.str.update(end - 1, end, '});');
1024
+ } else {
1025
+ state.str.update(
1026
+ /** @type {number} */ (node.start),
1027
+ start_end,
1028
+ `${state.names.run}(() => {\n${state.indent}`
1029
+ );
1030
+ state.str.indent(state.indent, {
1031
+ exclude: [
1032
+ [0, /** @type {number} */ (node.body.start)],
1033
+ [/** @type {number} */ (node.body.end), state.end]
1034
+ ]
1035
+ });
1036
+ state.str.appendLeft(/** @type {number} */ (node.end), `\n${state.indent}});`);
1037
+ }
1038
+ }
1039
+ };
1040
+
1041
+ /**
1042
+ *
1043
+ * @param {State} state
1044
+ * @param {number} start
1045
+ * @param {number} end
1046
+ */
1047
+ function trim_block(state, start, end) {
1048
+ const original = state.str.snip(start, end).toString();
1049
+ const without_parens = original.substring(1, original.length - 1);
1050
+ if (without_parens.trim().length !== without_parens.length) {
1051
+ state.str.update(start + 1, end - 1, without_parens.trim());
1052
+ }
1053
+ }
1054
+
1055
+ /** @type {Visitors<AST.SvelteNode, State>} */
1056
+ const template = {
1057
+ Identifier(node, { state, path }) {
1058
+ handle_identifier(node, state, path);
1059
+ },
1060
+ RegularElement(node, { state, path, next }) {
1061
+ // Strip off any namespace from the beginning of the node name.
1062
+ const node_name = node.name.replace(/[a-zA-Z-]*:/g, '');
1063
+
1064
+ if (state.analysis.source[node.end - 2] === '/' && !is_void(node_name) && !is_svg(node_name)) {
1065
+ let trimmed_position = node.end - 2;
1066
+ while (state.str.original.charAt(trimmed_position - 1) === ' ') trimmed_position--;
1067
+ state.str.remove(trimmed_position, node.end - 1);
1068
+ state.str.appendLeft(node.end, `</${node.name}>`);
1069
+ }
1070
+
1071
+ migrate_slot_usage(node, path, state);
1072
+ handle_events(node, state);
1073
+
1074
+ next();
1075
+ },
1076
+ SvelteSelf(node, { state, next }) {
1077
+ const source = state.str.original.substring(node.start, node.end);
1078
+ if (!state.filename) {
1079
+ const indent = guess_indent(source);
1080
+ has_migration_task = true;
1081
+ state.str.prependRight(
1082
+ node.start,
1083
+ `<!-- @migration-task: svelte:self is deprecated, import this Svelte file into itself instead -->\n${indent}`
1084
+ );
1085
+ next();
1086
+ return;
1087
+ }
1088
+ // overwrite the open tag
1089
+ state.str.overwrite(
1090
+ node.start + 1,
1091
+ node.start + 1 + 'svelte:self'.length,
1092
+ `${state.analysis.name}`
1093
+ );
1094
+ // if it has a fragment we need to overwrite the closing tag too
1095
+ if (node.fragment.nodes.length > 0) {
1096
+ state.str.overwrite(
1097
+ state.str.original.lastIndexOf('<', node.end) + 2,
1098
+ node.end - 1,
1099
+ `${state.analysis.name}`
1100
+ );
1101
+ } else if (!source.endsWith('/>')) {
1102
+ // special case for case `<svelte:self></svelte:self>` it has no fragment but
1103
+ // we still need to overwrite the end tag
1104
+ state.str.overwrite(
1105
+ node.start + source.lastIndexOf('</', node.end) + 2,
1106
+ node.end - 1,
1107
+ `${state.analysis.name}`
1108
+ );
1109
+ }
1110
+ state.has_svelte_self = true;
1111
+ next();
1112
+ },
1113
+ SvelteElement(node, { state, path, next }) {
1114
+ migrate_slot_usage(node, path, state);
1115
+ if (node.tag.type === 'Literal') {
1116
+ let is_static = true;
1117
+
1118
+ let a = /** @type {number} */ (node.tag.start);
1119
+ let b = /** @type {number} */ (node.tag.end);
1120
+ let quote_mark = state.str.original[a - 1];
1121
+
1122
+ while (state.str.original[--a] !== '=') {
1123
+ if (state.str.original[a] === '{') {
1124
+ is_static = false;
1125
+ break;
1126
+ }
1127
+ }
1128
+
1129
+ if (is_static && state.str.original[b] === quote_mark) {
1130
+ state.str.prependLeft(a + 1, '{');
1131
+ state.str.appendRight(/** @type {number} */ (node.tag.end) + 1, '}');
1132
+ }
1133
+ }
1134
+
1135
+ handle_events(node, state);
1136
+ next();
1137
+ },
1138
+ Component(node, { state, path, next }) {
1139
+ next();
1140
+ migrate_slot_usage(node, path, state);
1141
+ },
1142
+ SvelteComponent(node, { state, next, path }) {
1143
+ next();
1144
+
1145
+ migrate_slot_usage(node, path, state);
1146
+
1147
+ let expression = state.str
1148
+ .snip(
1149
+ /** @type {number} */ (node.expression.start),
1150
+ /** @type {number} */ (node.expression.end)
1151
+ )
1152
+ .toString();
1153
+
1154
+ if (
1155
+ (node.expression.type !== 'Identifier' && node.expression.type !== 'MemberExpression') ||
1156
+ !regex_valid_component_name.test(expression)
1157
+ ) {
1158
+ let current_expression = expression;
1159
+ expression = state.scope.generate('SvelteComponent');
1160
+ let needs_derived = true;
1161
+ for (let i = path.length - 1; i >= 0; i--) {
1162
+ const part = path[i];
1163
+ if (
1164
+ part.type === 'EachBlock' ||
1165
+ part.type === 'AwaitBlock' ||
1166
+ part.type === 'IfBlock' ||
1167
+ part.type === 'SnippetBlock' ||
1168
+ part.type === 'Component' ||
1169
+ part.type === 'SvelteComponent'
1170
+ ) {
1171
+ let position = node.start;
1172
+ if (i !== path.length - 1) {
1173
+ for (let modifier = 1; modifier < path.length - i; modifier++) {
1174
+ const path_part = path[i + modifier];
1175
+ if ('start' in path_part) {
1176
+ position = /** @type {number} */ (path_part.start);
1177
+ break;
1178
+ }
1179
+ }
1180
+ }
1181
+ const indent = state.str.original.substring(
1182
+ state.str.original.lastIndexOf('\n', position) + 1,
1183
+ position
1184
+ );
1185
+ state.str.appendRight(
1186
+ position,
1187
+ `{@const ${expression} = ${current_expression}}\n${indent}`
1188
+ );
1189
+ needs_derived = false;
1190
+ break;
1191
+ }
1192
+ }
1193
+ if (needs_derived) {
1194
+ if (state.derived_components.has(current_expression)) {
1195
+ expression = /** @type {string} */ (state.derived_components.get(current_expression));
1196
+ } else {
1197
+ state.derived_components.set(current_expression, expression);
1198
+ }
1199
+ }
1200
+ }
1201
+
1202
+ state.str.overwrite(node.start + 1, node.start + node.name.length + 1, expression);
1203
+
1204
+ if (state.str.original.substring(node.end - node.name.length - 1, node.end - 1) === node.name) {
1205
+ state.str.overwrite(node.end - node.name.length - 1, node.end - 1, expression);
1206
+ }
1207
+ let this_pos = state.str.original.lastIndexOf('this', node.expression.start);
1208
+ while (!state.str.original.charAt(this_pos - 1).trim()) this_pos--;
1209
+ const end_pos = state.str.original.indexOf('}', node.expression.end) + 1;
1210
+ state.str.remove(this_pos, end_pos);
1211
+ },
1212
+ SvelteFragment(node, { state, path, next }) {
1213
+ migrate_slot_usage(node, path, state);
1214
+ next();
1215
+ },
1216
+ SvelteWindow(node, { state, next }) {
1217
+ handle_events(node, state);
1218
+ next();
1219
+ },
1220
+ SvelteBody(node, { state, next }) {
1221
+ handle_events(node, state);
1222
+ next();
1223
+ },
1224
+ SvelteDocument(node, { state, next }) {
1225
+ handle_events(node, state);
1226
+ next();
1227
+ },
1228
+ SlotElement(node, { state, path, next, visit }) {
1229
+ migrate_slot_usage(node, path, state);
1230
+
1231
+ if (state.analysis.custom_element) return;
1232
+ let name = 'children';
1233
+ let slot_name = 'default';
1234
+ let slot_props = '{ ';
1235
+ let aliased_slot_name;
1236
+
1237
+ for (const attr of node.attributes) {
1238
+ if (attr.type === 'SpreadAttribute') {
1239
+ slot_props += `...${state.str.original.substring(/** @type {number} */ (attr.expression.start), attr.expression.end)}, `;
1240
+ } else if (attr.type === 'Attribute') {
1241
+ if (attr.name === 'slot') {
1242
+ continue;
1243
+ }
1244
+
1245
+ if (attr.name === 'name') {
1246
+ slot_name = /** @type {any} */ (attr.value)[0].data;
1247
+ // if some of the parents or this node itself har a slot
1248
+ // attribute with the sane name of this slot
1249
+ // we want to create a derived or the migrated snippet
1250
+ // will shadow the slot prop
1251
+ if (
1252
+ path.some(
1253
+ (parent) =>
1254
+ (parent.type === 'RegularElement' ||
1255
+ parent.type === 'SvelteElement' ||
1256
+ parent.type === 'Component' ||
1257
+ parent.type === 'SvelteComponent' ||
1258
+ parent.type === 'SvelteFragment') &&
1259
+ parent.attributes.some(
1260
+ (attribute) =>
1261
+ attribute.type === 'Attribute' &&
1262
+ attribute.name === 'slot' &&
1263
+ is_text_attribute(attribute) &&
1264
+ attribute.value[0].data === slot_name
1265
+ )
1266
+ ) ||
1267
+ node.attributes.some(
1268
+ (attribute) =>
1269
+ attribute.type === 'Attribute' &&
1270
+ attribute.name === 'slot' &&
1271
+ is_text_attribute(attribute) &&
1272
+ attribute.value[0].data === slot_name
1273
+ )
1274
+ ) {
1275
+ aliased_slot_name = `${slot_name}_render`;
1276
+ state.derived_conflicting_slots.set(aliased_slot_name, slot_name);
1277
+ }
1278
+ } else {
1279
+ const attr_value =
1280
+ attr.value === true || Array.isArray(attr.value) ? attr.value : [attr.value];
1281
+ let value = 'true';
1282
+ if (attr_value !== true) {
1283
+ const first = attr_value[0];
1284
+ const last = attr_value[attr_value.length - 1];
1285
+ for (const attr of attr_value) {
1286
+ visit(attr);
1287
+ }
1288
+ value = state.str
1289
+ .snip(
1290
+ first.type === 'Text'
1291
+ ? first.start - 1
1292
+ : /** @type {number} */ (first.expression.start),
1293
+ last.type === 'Text' ? last.end + 1 : /** @type {number} */ (last.expression.end)
1294
+ )
1295
+ .toString();
1296
+ }
1297
+ slot_props += value === attr.name ? `${value}, ` : `${attr.name}: ${value}, `;
1298
+ }
1299
+ }
1300
+ }
1301
+
1302
+ slot_props += '}';
1303
+ if (slot_props === '{ }') {
1304
+ slot_props = '';
1305
+ }
1306
+
1307
+ const existing_prop = state.props.find((prop) => prop.slot_name === slot_name);
1308
+ if (existing_prop) {
1309
+ name = existing_prop.local;
1310
+ } else if (slot_name !== 'default') {
1311
+ name = state.scope.generate(slot_name);
1312
+ if (name !== slot_name) {
1313
+ throw new MigrationError(
1314
+ `This migration would change the name of a slot (${slot_name} to ${name}) making the component unusable`
1315
+ );
1316
+ }
1317
+ }
1318
+
1319
+ if (!existing_prop) {
1320
+ state.props.push({
1321
+ local: name,
1322
+ exported: name,
1323
+ init: '',
1324
+ bindable: false,
1325
+ optional: true,
1326
+ slot_name,
1327
+ type: `import('svelte').${slot_props ? 'Snippet<[any]>' : 'Snippet'}`
1328
+ });
1329
+ } else if (existing_prop.needs_refine_type) {
1330
+ existing_prop.type = `import('svelte').${slot_props ? 'Snippet<[any]>' : 'Snippet'}`;
1331
+ existing_prop.needs_refine_type = false;
1332
+ }
1333
+
1334
+ if (
1335
+ slot_name === 'default' &&
1336
+ path.some(
1337
+ (parent) =>
1338
+ (parent.type === 'SvelteComponent' ||
1339
+ parent.type === 'Component' ||
1340
+ parent.type === 'RegularElement' ||
1341
+ parent.type === 'SvelteElement' ||
1342
+ parent.type === 'SvelteFragment') &&
1343
+ parent.attributes.some((attr) => attr.type === 'LetDirective')
1344
+ )
1345
+ ) {
1346
+ aliased_slot_name = `${name}_render`;
1347
+ state.derived_conflicting_slots.set(aliased_slot_name, name);
1348
+ }
1349
+ name = aliased_slot_name ?? name;
1350
+
1351
+ if (node.fragment.nodes.length > 0) {
1352
+ next();
1353
+ state.str.update(
1354
+ node.start,
1355
+ node.fragment.nodes[0].start,
1356
+ `{#if ${name}}{@render ${state.analysis.uses_props ? `${state.names.props}.` : ''}${name}(${slot_props})}{:else}`
1357
+ );
1358
+ state.str.update(node.fragment.nodes[node.fragment.nodes.length - 1].end, node.end, '{/if}');
1359
+ } else {
1360
+ state.str.update(
1361
+ node.start,
1362
+ node.end,
1363
+ `{@render ${state.analysis.uses_props ? `${state.names.props}.` : ''}${name}?.(${slot_props})}`
1364
+ );
1365
+ }
1366
+ },
1367
+ Comment(node, { state }) {
1368
+ const migrated = migrate_svelte_ignore(node.data);
1369
+ if (migrated !== node.data) {
1370
+ state.str.overwrite(node.start + '<!--'.length, node.end - '-->'.length, migrated);
1371
+ }
1372
+ },
1373
+ HtmlTag(node, { state, next }) {
1374
+ trim_block(state, node.start, node.end);
1375
+ next();
1376
+ },
1377
+ ConstTag(node, { state, next }) {
1378
+ trim_block(state, node.start, node.end);
1379
+ next();
1380
+ },
1381
+ IfBlock(node, { state, next }) {
1382
+ const start = node.start;
1383
+ const end = state.str.original.indexOf('}', node.test.end) + 1;
1384
+ trim_block(state, start, end);
1385
+ next();
1386
+ },
1387
+ AwaitBlock(node, { state, next }) {
1388
+ const start = node.start;
1389
+ const end =
1390
+ state.str.original.indexOf(
1391
+ '}',
1392
+ node.pending !== null ? node.expression.end : node.value?.end
1393
+ ) + 1;
1394
+ trim_block(state, start, end);
1395
+ if (node.pending !== null) {
1396
+ const start = state.str.original.lastIndexOf('{', node.value?.start);
1397
+ const end = state.str.original.indexOf('}', node.value?.end) + 1;
1398
+ trim_block(state, start, end);
1399
+ }
1400
+ if (node.catch !== null) {
1401
+ const start = state.str.original.lastIndexOf('{', node.error?.start);
1402
+ const end = state.str.original.indexOf('}', node.error?.end) + 1;
1403
+ trim_block(state, start, end);
1404
+ }
1405
+ next();
1406
+ },
1407
+ KeyBlock(node, { state, next }) {
1408
+ const start = node.start;
1409
+ const end = state.str.original.indexOf('}', node.expression.end) + 1;
1410
+ trim_block(state, start, end);
1411
+ next();
1412
+ }
1413
+ };
1414
+
1415
+ /**
1416
+ * @param {AST.RegularElement | AST.SvelteElement | AST.SvelteComponent | AST.Component | AST.SlotElement | AST.SvelteFragment} node
1417
+ * @param {AST.SvelteNode[]} path
1418
+ * @param {State} state
1419
+ */
1420
+ function migrate_slot_usage(node, path, state) {
1421
+ const parent = path.at(-2);
1422
+ // Bail on custom element slot usage
1423
+ if (
1424
+ parent?.type !== 'Component' &&
1425
+ parent?.type !== 'SvelteComponent' &&
1426
+ node.type !== 'Component' &&
1427
+ node.type !== 'SvelteComponent'
1428
+ ) {
1429
+ return;
1430
+ }
1431
+
1432
+ let snippet_name = 'children';
1433
+ let snippet_props = [];
1434
+
1435
+ // if we stop the transform because the name is not correct we don't want to
1436
+ // remove the let directive and they could come before the name
1437
+ let removal_queue = [];
1438
+
1439
+ for (let attribute of node.attributes) {
1440
+ if (
1441
+ attribute.type === 'Attribute' &&
1442
+ attribute.name === 'slot' &&
1443
+ is_text_attribute(attribute)
1444
+ ) {
1445
+ snippet_name = attribute.value[0].data;
1446
+ // the default slot in svelte 4 if what the children slot is for svelte 5
1447
+ if (snippet_name === 'default') {
1448
+ snippet_name = 'children';
1449
+ }
1450
+ if (!regex_is_valid_identifier.test(snippet_name) || is_reserved(snippet_name)) {
1451
+ has_migration_task = true;
1452
+ state.str.appendLeft(
1453
+ node.start,
1454
+ `<!-- @migration-task: migrate this slot by hand, \`${snippet_name}\` is an invalid identifier -->\n${state.indent}`
1455
+ );
1456
+ return;
1457
+ }
1458
+ if (parent?.type === 'Component' || parent?.type === 'SvelteComponent') {
1459
+ for (let attribute of parent.attributes) {
1460
+ if (attribute.type === 'Attribute' || attribute.type === 'BindDirective') {
1461
+ if (attribute.name === snippet_name) {
1462
+ state.str.appendLeft(
1463
+ node.start,
1464
+ `<!-- @migration-task: migrate this slot by hand, \`${snippet_name}\` would shadow a prop on the parent component -->\n${state.indent}`
1465
+ );
1466
+ return;
1467
+ }
1468
+ }
1469
+ }
1470
+ }
1471
+ // flush the queue after we found the name
1472
+ for (let remove_let of removal_queue) {
1473
+ remove_let();
1474
+ }
1475
+ state.str.remove(attribute.start, attribute.end);
1476
+ }
1477
+ if (attribute.type === 'LetDirective') {
1478
+ snippet_props.push(
1479
+ attribute.name +
1480
+ (attribute.expression
1481
+ ? `: ${state.str.original.substring(/** @type {number} */ (attribute.expression.start), /** @type {number} */ (attribute.expression.end))}`
1482
+ : '')
1483
+ );
1484
+ // we just add to the queue to remove them after we found if we need to migrate or we bail
1485
+ removal_queue.push(() => state.str.remove(attribute.start, attribute.end));
1486
+ }
1487
+ }
1488
+
1489
+ if (removal_queue.length > 0) {
1490
+ for (let remove_let of removal_queue) {
1491
+ remove_let();
1492
+ }
1493
+ }
1494
+
1495
+ if (node.type === 'SvelteFragment' && node.fragment.nodes.length > 0) {
1496
+ // remove node itself, keep content
1497
+ state.str.remove(node.start, node.fragment.nodes[0].start);
1498
+ state.str.remove(node.fragment.nodes[node.fragment.nodes.length - 1].end, node.end);
1499
+ }
1500
+
1501
+ const props = snippet_props.length > 0 ? `{ ${snippet_props.join(', ')} }` : '';
1502
+
1503
+ if (snippet_name === 'children' && node.type !== 'SvelteFragment') {
1504
+ if (snippet_props.length === 0) return; // nothing to do
1505
+
1506
+ let inner_start = 0;
1507
+ let inner_end = 0;
1508
+ for (let i = 0; i < node.fragment.nodes.length; i++) {
1509
+ const inner = node.fragment.nodes[i];
1510
+ const is_empty_text = inner.type === 'Text' && !inner.data.trim();
1511
+
1512
+ if (
1513
+ (inner.type === 'RegularElement' ||
1514
+ inner.type === 'SvelteElement' ||
1515
+ inner.type === 'Component' ||
1516
+ inner.type === 'SvelteComponent' ||
1517
+ inner.type === 'SlotElement' ||
1518
+ inner.type === 'SvelteFragment') &&
1519
+ inner.attributes.some((attr) => attr.type === 'Attribute' && attr.name === 'slot')
1520
+ ) {
1521
+ if (inner_start && !inner_end) {
1522
+ // End of default slot content
1523
+ inner_end = inner.start;
1524
+ }
1525
+ } else if (!inner_start && !is_empty_text) {
1526
+ // Start of default slot content
1527
+ inner_start = inner.start;
1528
+ } else if (inner_end && !is_empty_text) {
1529
+ // There was default slot content before, then some named slot content, now some default slot content again.
1530
+ // We're moving the last character back by one to avoid the closing {/snippet} tag inserted afterwards
1531
+ // to come before the opening {#snippet} tag of the named slot.
1532
+ state.str.update(inner_end - 1, inner_end, '');
1533
+ state.str.prependLeft(inner_end - 1, state.str.original[inner_end - 1]);
1534
+ state.str.move(inner.start, inner.end, inner_end - 1);
1535
+ }
1536
+ }
1537
+
1538
+ if (!inner_end) {
1539
+ inner_end = node.fragment.nodes[node.fragment.nodes.length - 1].end;
1540
+ }
1541
+
1542
+ state.str.appendLeft(
1543
+ inner_start,
1544
+ `{#snippet ${snippet_name}(${props})}\n${state.indent.repeat(path.length)}`
1545
+ );
1546
+ state.str.indent(state.indent, {
1547
+ exclude: [
1548
+ [0, inner_start],
1549
+ [inner_end, state.str.original.length]
1550
+ ]
1551
+ });
1552
+ if (inner_end < node.fragment.nodes[node.fragment.nodes.length - 1].end) {
1553
+ // Named slots coming afterwards
1554
+ state.str.prependLeft(inner_end, `{/snippet}\n${state.indent.repeat(path.length)}`);
1555
+ } else {
1556
+ // No named slots coming afterwards
1557
+ state.str.prependLeft(
1558
+ inner_end,
1559
+ `${state.indent.repeat(path.length)}{/snippet}\n${state.indent.repeat(path.length - 1)}`
1560
+ );
1561
+ }
1562
+ } else {
1563
+ // Named slot or `svelte:fragment`: wrap element itself in a snippet
1564
+ state.str.prependLeft(
1565
+ node.start,
1566
+ `{#snippet ${snippet_name}(${props})}\n${state.indent.repeat(path.length - 2)}`
1567
+ );
1568
+ state.str.indent(state.indent, {
1569
+ exclude: [
1570
+ [0, node.start],
1571
+ [node.end, state.str.original.length]
1572
+ ]
1573
+ });
1574
+ const str = `\n${state.indent.repeat(path.length - 2)}{/snippet}`;
1575
+
1576
+ if (node.type === 'SlotElement') {
1577
+ state.str.appendRight(node.end, str);
1578
+ } else {
1579
+ state.str.appendLeft(node.end, str);
1580
+ }
1581
+ }
1582
+ }
1583
+
1584
+ /**
1585
+ * @param {VariableDeclarator} declarator
1586
+ * @param {State} state
1587
+ * @param {AST.SvelteNode[]} path
1588
+ */
1589
+ function extract_type_and_comment(declarator, state, path) {
1590
+ const str = state.str;
1591
+ const parent = path.at(-1);
1592
+
1593
+ // Try to find jsdoc above the declaration
1594
+ let comment_node = /** @type {Node} */ (parent)?.leadingComments?.at(-1);
1595
+
1596
+ const comment_start = /** @type {any} */ (comment_node)?.start;
1597
+ const comment_end = /** @type {any} */ (comment_node)?.end;
1598
+ let comment = comment_node && str.original.substring(comment_start, comment_end);
1599
+ if (comment_node) {
1600
+ str.update(comment_start, comment_end, '');
1601
+ }
1602
+
1603
+ // Find trailing comments
1604
+ const trailing_comment_node = /** @type {Node} */ (parent)?.trailingComments?.at(0);
1605
+ const trailing_comment_start = /** @type {any} */ (trailing_comment_node)?.start;
1606
+ const trailing_comment_end = /** @type {any} */ (trailing_comment_node)?.end;
1607
+ let trailing_comment =
1608
+ trailing_comment_node && str.original.substring(trailing_comment_start, trailing_comment_end);
1609
+
1610
+ if (trailing_comment_node) {
1611
+ str.update(trailing_comment_start, trailing_comment_end, '');
1612
+ }
1613
+
1614
+ if (declarator.id.typeAnnotation) {
1615
+ state.has_type_or_fallback = true;
1616
+ let start = declarator.id.typeAnnotation.start + 1; // skip the colon
1617
+ while (str.original[start] === ' ') {
1618
+ start++;
1619
+ }
1620
+ return {
1621
+ type: str.original.substring(start, declarator.id.typeAnnotation.end),
1622
+ comment,
1623
+ trailing_comment
1624
+ };
1625
+ }
1626
+
1627
+ let cleaned_comment_arr = comment
1628
+ ?.split('\n')
1629
+ .map((line) =>
1630
+ line
1631
+ .trim()
1632
+ // replace `// ` for one liners
1633
+ .replace(/^\/\/\s*/g, '')
1634
+ // replace `\**` for the initial JSDoc
1635
+ .replace(/^\/\*\*?\s*/g, '')
1636
+ // migrate `*/` for the end of JSDoc
1637
+ .replace(/\s*\*\/$/g, '')
1638
+ // remove any initial `* ` to clean the comment
1639
+ .replace(/^\*\s*/g, '')
1640
+ )
1641
+ .filter(Boolean);
1642
+ const first_at_comment = cleaned_comment_arr?.findIndex((line) => line.startsWith('@'));
1643
+ let cleaned_comment = cleaned_comment_arr
1644
+ ?.slice(0, first_at_comment !== -1 ? first_at_comment : cleaned_comment_arr.length)
1645
+ .join('\n');
1646
+
1647
+ let cleaned_comment_arr_trailing = trailing_comment
1648
+ ?.split('\n')
1649
+ .map((line) =>
1650
+ line
1651
+ .trim()
1652
+ // replace `// ` for one liners
1653
+ .replace(/^\/\/\s*/g, '')
1654
+ // replace `\**` for the initial JSDoc
1655
+ .replace(/^\/\*\*?\s*/g, '')
1656
+ // migrate `*/` for the end of JSDoc
1657
+ .replace(/\s*\*\/$/g, '')
1658
+ // remove any initial `* ` to clean the comment
1659
+ .replace(/^\*\s*/g, '')
1660
+ )
1661
+ .filter(Boolean);
1662
+ const first_at_comment_trailing = cleaned_comment_arr_trailing?.findIndex((line) =>
1663
+ line.startsWith('@')
1664
+ );
1665
+ let cleaned_comment_trailing = cleaned_comment_arr_trailing
1666
+ ?.slice(
1667
+ 0,
1668
+ first_at_comment_trailing !== -1
1669
+ ? first_at_comment_trailing
1670
+ : cleaned_comment_arr_trailing.length
1671
+ )
1672
+ .join('\n');
1673
+
1674
+ // try to find a comment with a type annotation, hinting at jsdoc
1675
+ if (parent?.type === 'ExportNamedDeclaration' && comment_node) {
1676
+ state.has_type_or_fallback = true;
1677
+ const match = /@type {(.+)}/.exec(comment_node.value);
1678
+ if (match) {
1679
+ // try to find JSDoc comments after a hyphen `-`
1680
+ const jsdoc_comment = /@type {.+} (?:\w+|\[.*?\]) - (.+)/.exec(comment_node.value);
1681
+ if (jsdoc_comment) {
1682
+ cleaned_comment += jsdoc_comment[1]?.trim();
1683
+ }
1684
+ return {
1685
+ type: match[1],
1686
+ comment: cleaned_comment,
1687
+ trailing_comment: cleaned_comment_trailing
1688
+ };
1689
+ }
1690
+ }
1691
+
1692
+ // try to infer it from the init
1693
+ if (declarator.init?.type === 'Literal') {
1694
+ state.has_type_or_fallback = true; // only assume type if it's trivial to infer - else someone would've added a type annotation
1695
+ const type = typeof declarator.init.value;
1696
+ if (type === 'string' || type === 'number' || type === 'boolean') {
1697
+ return {
1698
+ type,
1699
+ comment: state.uses_ts ? comment : cleaned_comment,
1700
+ trailing_comment: state.uses_ts ? trailing_comment : cleaned_comment_trailing
1701
+ };
1702
+ }
1703
+ }
1704
+ return {
1705
+ type: 'any',
1706
+ comment: state.uses_ts ? comment : cleaned_comment,
1707
+ trailing_comment: state.uses_ts ? trailing_comment : cleaned_comment_trailing
1708
+ };
1709
+ }
1710
+
1711
+ // Ensure modifiers are applied in the same order as Svelte 4
1712
+ const modifier_order = /** @type {const} */ ([
1713
+ 'preventDefault',
1714
+ 'stopPropagation',
1715
+ 'stopImmediatePropagation',
1716
+ 'self',
1717
+ 'trusted',
1718
+ 'once'
1719
+ ]);
1720
+
1721
+ /**
1722
+ * @param {AST.RegularElement | AST.SvelteElement | AST.SvelteWindow | AST.SvelteDocument | AST.SvelteBody} element
1723
+ * @param {State} state
1724
+ */
1725
+ function handle_events(element, state) {
1726
+ /** @type {Map<string, AST.OnDirective[]>} */
1727
+ const handlers = new Map();
1728
+ for (const attribute of element.attributes) {
1729
+ if (attribute.type !== 'OnDirective') continue;
1730
+
1731
+ let name = `on${attribute.name}`;
1732
+ if (attribute.modifiers.includes('capture')) {
1733
+ name += 'capture';
1734
+ }
1735
+
1736
+ const nodes = handlers.get(name) || [];
1737
+ nodes.push(attribute);
1738
+ handlers.set(name, nodes);
1739
+ }
1740
+
1741
+ for (const [name, nodes] of handlers) {
1742
+ const handlers = [];
1743
+
1744
+ let first = null;
1745
+
1746
+ for (const node of nodes) {
1747
+ /** @type {string} */
1748
+ let body;
1749
+
1750
+ if (node.expression) {
1751
+ body = state.str.original.substring(
1752
+ /** @type {number} */ (node.expression.start),
1753
+ /** @type {number} */ (node.expression.end)
1754
+ );
1755
+ } else {
1756
+ body = `${state.names.bubble}('${node.name}')`;
1757
+ state.legacy_imports.add('createBubbler');
1758
+ state.script_insertions.add(
1759
+ `const ${state.names.bubble} = ${state.names.createBubbler}();`
1760
+ );
1761
+ }
1762
+
1763
+ const has_passive = node.modifiers.includes('passive');
1764
+ const has_nonpassive = node.modifiers.includes('nonpassive');
1765
+
1766
+ const modifiers = modifier_order.filter((modifier) => node.modifiers.includes(modifier));
1767
+
1768
+ for (const modifier of modifiers) {
1769
+ state.legacy_imports.add(modifier);
1770
+ body = `${state.names[modifier]}(${body})`;
1771
+ }
1772
+
1773
+ if (has_passive || has_nonpassive) {
1774
+ const action = has_passive ? 'passive' : 'nonpassive';
1775
+ state.legacy_imports.add(action);
1776
+
1777
+ state.str.overwrite(
1778
+ node.start,
1779
+ node.end,
1780
+ `use:${state.names[action]}={['${node.name}', () => ${body}]}`
1781
+ );
1782
+ } else {
1783
+ if (first) {
1784
+ let start = node.start;
1785
+ let end = node.end;
1786
+
1787
+ while (/[\s\n]/.test(state.str.original[start - 1])) start -= 1;
1788
+ state.str.remove(start, end);
1789
+ } else {
1790
+ first = node;
1791
+ }
1792
+
1793
+ handlers.push(body);
1794
+ }
1795
+ }
1796
+
1797
+ if (first) {
1798
+ /** @type {string} */
1799
+ let replacement;
1800
+
1801
+ if (handlers.length > 1) {
1802
+ state.legacy_imports.add('handlers');
1803
+ replacement = `${name}={${state.names.handlers}(${handlers.join(', ')})}`;
1804
+ } else {
1805
+ const handler = handlers[0];
1806
+ replacement = handler === name ? `{${handler}}` : `${name}={${handler}}`;
1807
+ }
1808
+
1809
+ state.str.overwrite(first.start, first.end, replacement);
1810
+ }
1811
+ }
1812
+ }
1813
+
1814
+ /**
1815
+ * Returns start and end of the node. If the start is preceded with white-space-only before a line break,
1816
+ * the start will be the start of the line.
1817
+ * @param {string} source
1818
+ * @param {LabeledStatement} node
1819
+ */
1820
+ function get_node_range(source, node) {
1821
+ const first_leading_comment = node.leadingComments?.[0];
1822
+ const last_trailing_comment = node.trailingComments?.[node.trailingComments.length - 1];
1823
+
1824
+ // @ts-expect-error the type of `Comment` seems to be wrong...the node actually contains
1825
+ // start and end but the type seems to only contain a `range` (which doesn't actually exists)
1826
+ let start = /** @type {number} */ (first_leading_comment?.start ?? node.start);
1827
+ // @ts-expect-error the type of `Comment` seems to be wrong...the node actually contains
1828
+ // start and end but the type seems to only contain a `range` (which doesn't actually exists)
1829
+ let end = /** @type {number} */ (last_trailing_comment?.end ?? node.end);
1830
+
1831
+ let idx = start;
1832
+ while (source[idx - 1] !== '\n' && source[idx - 1] !== '\r') {
1833
+ idx--;
1834
+ if (source[idx] !== ' ' && source[idx] !== '\t') {
1835
+ idx = start;
1836
+ break;
1837
+ }
1838
+ }
1839
+
1840
+ start = idx;
1841
+
1842
+ return { start, end };
1843
+ }
1844
+
1845
+ /**
1846
+ * @param {Identifier} node
1847
+ * @param {State} state
1848
+ * @param {any[]} path
1849
+ */
1850
+ function handle_identifier(node, state, path) {
1851
+ const parent = path.at(-1);
1852
+ if (parent?.type === 'MemberExpression' && parent.property === node) return;
1853
+
1854
+ if (state.analysis.uses_props && node.name !== '$$slots') {
1855
+ if (node.name === '$$props' || node.name === '$$restProps') {
1856
+ // not 100% correct for $$restProps but it'll do
1857
+ state.str.update(
1858
+ /** @type {number} */ (node.start),
1859
+ /** @type {number} */ (node.end),
1860
+ state.names.props
1861
+ );
1862
+ } else {
1863
+ const binding = state.scope.get(node.name);
1864
+ if (binding?.kind === 'bindable_prop' && binding.node !== node) {
1865
+ state.str.prependLeft(/** @type {number} */ (node.start), `${state.names.props}.`);
1866
+ }
1867
+ }
1868
+ } else if (node.name === '$$restProps' && state.analysis.uses_rest_props) {
1869
+ state.str.update(
1870
+ /** @type {number} */ (node.start),
1871
+ /** @type {number} */ (node.end),
1872
+ state.names.rest
1873
+ );
1874
+ } else if (node.name === '$$slots' && state.analysis.uses_slots) {
1875
+ if (parent?.type === 'MemberExpression') {
1876
+ if (state.analysis.custom_element) return;
1877
+
1878
+ let name = parent.property.type === 'Literal' ? parent.property.value : parent.property.name;
1879
+ let slot_name = name;
1880
+ const existing_prop = state.props.find((prop) => prop.slot_name === name);
1881
+ if (existing_prop) {
1882
+ name = existing_prop.local;
1883
+ } else if (name !== 'default') {
1884
+ let new_name = state.scope.generate(name);
1885
+ if (new_name !== name) {
1886
+ throw new MigrationError(
1887
+ `This migration would change the name of a slot (${name} to ${new_name}) making the component unusable`
1888
+ );
1889
+ }
1890
+ }
1891
+
1892
+ name = name === 'default' ? 'children' : name;
1893
+
1894
+ if (!existing_prop) {
1895
+ state.props.push({
1896
+ local: name,
1897
+ exported: name,
1898
+ init: '',
1899
+ bindable: false,
1900
+ optional: true,
1901
+ slot_name,
1902
+ // if it's the first time we encounter this slot
1903
+ // we start with any and delegate to when the slot
1904
+ // is actually rendered (it might not happen in that case)
1905
+ // any is still a safe bet
1906
+ type: `import('svelte').Snippet<[any]>`,
1907
+ needs_refine_type: true
1908
+ });
1909
+ }
1910
+
1911
+ state.str.update(
1912
+ /** @type {number} */ (node.start),
1913
+ parent.property.start,
1914
+ state.analysis.uses_props ? `${state.names.props}.` : ''
1915
+ );
1916
+ state.str.update(parent.property.start, parent.end, name);
1917
+ }
1918
+ // else passed as identifier, we don't know what to do here, so let it error
1919
+ } else if (
1920
+ parent?.type === 'TSInterfaceDeclaration' ||
1921
+ parent?.type === 'TSTypeAliasDeclaration'
1922
+ ) {
1923
+ const members =
1924
+ parent.type === 'TSInterfaceDeclaration' ? parent.body.body : parent.typeAnnotation?.members;
1925
+ if (Array.isArray(members)) {
1926
+ if (node.name === '$$Props') {
1927
+ state.has_type_or_fallback = true;
1928
+
1929
+ for (const member of members) {
1930
+ const prop = state.props.find((prop) => prop.exported === member.key.name);
1931
+
1932
+ const type = state.str.original.substring(
1933
+ member.typeAnnotation.typeAnnotation.start,
1934
+ member.typeAnnotation.typeAnnotation.end
1935
+ );
1936
+
1937
+ let comment;
1938
+ const comment_node = member.leadingComments?.at(-1);
1939
+ if (comment_node?.type === 'Block') {
1940
+ comment = state.str.original.substring(comment_node.start, comment_node.end);
1941
+ }
1942
+
1943
+ const trailing_comment = member.trailingComments?.at(0)?.value;
1944
+
1945
+ if (prop) {
1946
+ prop.type = type;
1947
+ prop.optional = member.optional;
1948
+ prop.comment = comment ?? prop.comment;
1949
+ prop.trailing_comment = trailing_comment ?? prop.trailing_comment;
1950
+ } else {
1951
+ state.props.push({
1952
+ local: member.key.name,
1953
+ exported: member.key.name,
1954
+ init: '',
1955
+ bindable: false,
1956
+ optional: member.optional,
1957
+ type,
1958
+ comment,
1959
+ trailing_comment,
1960
+ type_only: true
1961
+ });
1962
+ }
1963
+ }
1964
+
1965
+ state.str.remove(parent.start, parent.end);
1966
+ }
1967
+ }
1968
+ }
1969
+ }
1970
+
1971
+ /** @param {string} content */
1972
+ function guess_indent(content) {
1973
+ const lines = content.split('\n');
1974
+
1975
+ const tabbed = lines.filter((line) => /^\t+/.test(line));
1976
+ const spaced = lines.filter((line) => /^ {2,}/.test(line));
1977
+
1978
+ if (tabbed.length === 0 && spaced.length === 0) {
1979
+ return '\t';
1980
+ }
1981
+
1982
+ // More lines tabbed than spaced? Assume tabs, and
1983
+ // default to tabs in the case of a tie (or nothing
1984
+ // to go on)
1985
+ if (tabbed.length >= spaced.length) {
1986
+ return '\t';
1987
+ }
1988
+
1989
+ // Otherwise, we need to guess the multiple
1990
+ const min = spaced.reduce((previous, current) => {
1991
+ const count = /^ +/.exec(current)?.[0].length ?? 0;
1992
+ return Math.min(count, previous);
1993
+ }, Infinity);
1994
+
1995
+ return ' '.repeat(min);
1996
+ }