
Svelte 编译器script编译错误全解52 个 script 错误码、触发条件与源码级排查指南【免费下载链接】svelteweb development for the rest of us项目地址: https://gitcode.com/GitHub_Trending/sv/svelte本文基于 Svelte 仓库中packages/svelte/messages/compile-errors/script.md这份错误消息定义文档完整梳理 Svelte 编译器在script含script module层面会抛出的全部编译错误每个错误码的含义、触发场景、修复方式以及它们在编译器2-analyze阶段的真实触发点。读完本文你可以准确读懂每一条CompileError的报错信息定位对应的源码校验逻辑并通过测试样例目录复现和验证。错误消息是如何生成与抛出的理解这份错误清单前先看它的生产管线这决定了你在报错信息中看到的固定格式错误文案以 Markdown 形式维护在 messages/compile-errors/script.md 中每个错误以## 错误码小节定义消息模板%name%形式的占位符在抛出时被替换为实际值如%rune%、%name%、%cycle%。仓库通过 scripts/process-messages/templates/compile-errors.js 模板把每个小节转换成导出函数。该文件头部注明「This file is generated by scripts/process-messages/index.js. Do not edit!」——真正的可执行版本被生成到 src/compiler/errors.js。每个生成函数形如state_invalid_placement(node, rune)内部调用e(node, state_invalid_placement, 消息)构造一个继承自Error的InternalCompileErrorname为CompileError并携带[start, end]源码位置最后以模板字符串形式抛出且每条消息尾部都追加https://svelte.dev/e/错误码形式的帮助链接。从源码结构看这意味着报错信息可以直接被 bundler 插件捕获构造函数注释里明确说明了「extend from Error so that various bundler plugins properly handle it」。因此实际开发中遇到CompileError: $props() can only be used at the top level...时错误码即对应本文档中某个小节标题帮助链接指向官方错误文档页。下面按主题分组完整覆盖文档中的全部 52 个错误码。分组速查表1. Props 与$props()/$bindable()校验错误码消息触发场景props_invalid_placement$props()can only be used at the top level of components as a variable declaration initializer$props()没写在组件实例脚本顶层的变量声明初始化位置例如写进script module或函数内props_duplicateCannot use%rune%()more than once同一组件中多次调用$props()或对$props.id()重复调用props_invalid_identifier$props()can only be used with an object destructuring patternlet props $props()而不是对象解构let { a } $props()props_invalid_pattern$props()assignment must not contain nested properties or computed keys解构模式里出现嵌套属性或计算键props_id_invalid_placement$props.id()can only be used at the top level of components as a variable declaration initializer$props.id()未在顶层以简单标识符声明props_illegal_nameDeclaring or accessing a prop starting with$$is illegal (they are reserved for Svelte internals)声明或访问以$$开头的 propbindable_invalid_location$bindable()can only be used inside a$props()declaration$bindable()出现在$props()声明之外源码印证phases/2-analyze/visitors/CallExpression.js 中对$props的三条校验与消息逐条对应——先用context.state.has_props_rune判重props_duplicate再校验父节点必须是VariableDeclarator、所在脚本类型是instance、且处于实例脚本作用域根props_invalid_placement最后要求$props()不接受任何实参复用rune_invalid_arguments。对$bindable()同文件 L42-L52 沿 AST 路径向上回溯三层确认它必须位于$props()解构模式内的AssignmentPattern中否则抛bindable_invalid_location。2. Runes 调用形态校验$state/$derived/$effect等错误码消息说明state_invalid_placement%rune%(...)can only be used as a variable declaration initializer, a class field declaration, or the first assignment to a class field at the top level of the constructor.$state、$state.raw、$derived、$derived.by只能出现在三种位置effect_invalid_placement$effect()can only be used as an expression statement$effect/$effect.pre不能作为表达式值传递必须独立成句rune_invalid_arguments%rune%cannot be called with arguments对不接受参数的 rune如$host、$effect.tracking传了实参rune_invalid_arguments_length%rune%must be called with %args%实参数量不对。从源码看$derived/$derived.by要求「exactly one argument」其余 rune 要求「zero or one arguments」CallExpression.js L128-L132rune_invalid_computed_propertyCannot access a computed property of a rune形如$state[raw]的括号动态访问rune_invalid_name%name%is not a valid rune引用了不存在的 rune如$states、$state.xyzrune_invalid_spread%rune%cannot be called with a spread argument对 rune 使用展开调用rune_invalid_usageCannot use%rune%rune in non-runes mode在非 runes 模式组件中使用 runerune_missing_parenthesesCannot use rune without parentheses裸引用 rune 而不带括号rune_removedThe%name%rune has been removed已移除的 rune。Identifier.js L65-L67 中硬编码针对$state.is报此错rune_renamed%name%is now%replacement%已更名的 rune。源码中对$effect.active→$effect.tracking、$state.frozen→$state.raw专门给出替换建议Identifier.js L57-L63rune_missing_parentheses在原文档中有专门展开Runes 是关键字而非值不能被赋值或作为参数传递只能被调用因此不带括号引用即报错let count $state;无论是 rune 本身$state还是通过属性到达的 rune$derived.by都要加上括号及所需参数let count $state(0);触发点印证visitors/Identifier.js L40-L76 中当标识符名符合 rune 形态、且作用域中没有同名变量遮蔽时编译器沿父链向上合并MemberExpression拼出完整 rune 名逐一校验若最终父节点不是CallExpression就抛rune_missing_parentheses。一个特殊案例来自 visitors/EachBlock.js L21-L24{#each items as $state}这种把 rune 名当作 each 块参数的写法会直接复用state_invalid_placement报错源码中还留有一条// TODO weird that this is necessary注释。3. State 字段class 中的 rune 声明错误码消息说明state_field_duplicate%name%has already been declared on this class同一个字段被两次声明为 state 字段state_field_invalid_assignmentCannot assign to a state field before its declaration在字段声明之前给它赋值如构造函数内先赋值后声明state_invalid_exportCannot export state from a module if it is reassigned. Either export a function returning the state value or only mutate the state values properties模块中被重新赋值的$state不能直接导出derived_invalid_exportCannot export derived state from a module. To expose the current derived value, export a function returning its value模块中的 derived 状态只能以函数形式对外暴露state_field_duplicate原文档给出了完整示例。对使用了$state或$derived的 class 字段赋值被视为state 字段声明可以写在类体内class Counter { count $state(0); }也可以写在构造函数里class Counter { constructor() { this.count $state(0); } }但同一字段只能声明一次重复即报state_field_duplicate。触发逻辑见 visitors/PropertyDefinition.js L10-L18按字段名在state_fields中查找已有记录若当前节点不是原始声明节点且带值还会比较start位置——先赋值后声明则抛state_field_invalid_assignment。4. 命名与声明规则错误码消息说明declaration_duplicate%name%has already been declared同一作用域重复声明同名变量declaration_duplicate_module_importCannot declare a variable with the same name as an import fromscript module实例脚本变量与模块级 import 重名duplicate_class_field%name%has already been declared类字段重名constant_assignmentCannot assign to %thing%给常量如const、import 等赋值constant_bindingCannot bind to %thing%把bind:目标指向常量dollar_binding_invalidThe $ name is reserved, and cannot be used for variables and imports变量或 import 恰好叫$dollar_prefix_invalidThe $ prefix is reserved, and cannot be used for variables and imports变量或 import 以$开头runes 模式下$前缀被保留global_reference_invalid%name%is an illegal variable name. To reference a global variable called%name%, useglobalThis.%name%想要引用形如$foo的全局变量export_undefined%name%is not defined导出了未定义的名称invalid_arguments_usageThe arguments keyword cannot be used within the template or at the top level of a component在模板或组件顶层使用argumentsmodule_illegal_default_exportA component cannot have a default export.svelte组件中出现export defaultreactive_declaration_cycleCyclical dependency detected: %cycle%检测出循环依赖%cycle% 会列出具体链条global_reference_invalid有一条现成的测试样例 tests/compiler-errors/samples/store-global-disallowed断言报错消息为$foo is an illegal variable name. To reference a global variable called $foo, use globalThis.$foo可直接用于复现。5.each块与 snippet 参数不可变迭代目标each_item_invalid_assignmentCannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. \array[i] value instead of entry value, or bind:value{array[i]} instead of bind:value{entry})原文档对这条错误做了完整的历史对比。Legacy 模式下允许直接给 each 块参数重新赋值或双向绑定script let array [1, 2, 3]; /script {#each array as entry} !-- reassignment -- button on:click{() entry 4}change/button !-- binding -- input bind:value{entry} {/each}这种做法后来被证明有缺陷、行为不可预测尤其是在array.map(...)等派生值上遍历时因此在 runes 模式中被禁止。等价写法是用下标代替script let array $state([1, 2, 3]); /script {#each array as entry, i} !-- reassignment -- button onclick{() array[i] 4}change/button !-- binding -- input bind:value{array[i]} {/each}对应测试 tests/compiler-errors/samples/runes-invalid-each-binding 校验的正是这条消息原文。snippet_parameter_assignmentCannot reassign or bind to snippet parameter。snippet 参数与 each 块参数同理是不可重新赋值、不可绑定的。6. legacy 模式与 runes 模式的互斥规则错误码消息说明legacy_export_invalidCannot useexport letin runes mode — use$props()insteadrunes 组件中使用了export letlegacy_props_invalidCannot use$$propsin runes moderunes 组件中引用$$propslegacy_rest_props_invalidCannot use$$restPropsin runes moderunes 组件中引用$$restPropslegacy_reactive_statement_invalid$:is not allowed in runes mode, use$derivedor$effectinsteadrunes 组件中使用了$:响应式语句legacy_await_invalidCannot useawaitin deriveds and template expressions, or at the top level of a component, unless in runes mode非 runes 模式下在 derived、模板表达式或组件顶层使用awaitexperimental_asyncCannot useawaitin deriveds and template expressions, or at the top level of a component, unless theexperimental.asynccompiler option istruerunes 模式下上述await用法需要显式开启experimental.async编译选项runes_mode_invalid_import%name% cannot be used in runes mode在 runes 模式导入了与之冲突的旧式 API如 legacy 生命周期函数从源码看legacy 标识符在 Identifier.js L81-L89 中还有专门处理非 runes 模式引用$$props/$$restProps时会设置analysis.uses_props/uses_rest_props标记供后续 transform 阶段生成 props 对象使用。7. Store 订阅$前缀自动订阅错误码消息store_invalid_scoped_subscriptionCannot subscribe to stores that are not declared at the top level of the componentstore_invalid_subscriptionCannot reference store value insidescript modulestore_invalid_subscription_moduleCannot reference store value outside a.sveltefilestore_invalid_subscription_module原文档有补充解释用$前缀引用 store 值只在.svelte文件内有效因为只有在那里 Svelte 才能自动在组件挂载时创建订阅、卸载时取消订阅文档同时建议考虑迁移到 runes。三条错误的共同背景自动订阅依赖组件生命周期管理因此 store 必须声明在组件顶层、且不能在script module无组件实例或.svelte文件之外使用$store语法。8. 其他专项错误码消息说明host_invalid_placement$host()can only be used inside custom element component instances$host()只能在customElement组件实例中使用且不能带参数CallExpression.js L59-L66 同时校验参数为空和custom_element标记inspect_trace_generator$inspect.trace(...)cannot be used inside a generator function生成器函数中无法使用 traceinspect_trace_invalid_placement$inspect.trace(...)must be the first statement of a function bodytrace 必须是函数体第一条语句snippet_invalid_exportAn exported snippet can only reference things declared in ascript module, or other exportable snippets导出 snippet 的作用域限制import_svelte_internal_forbiddenImports ofsvelte/internal/*are forbidden...禁止导入内部运行时代码因其属于私有实现且随时可能变更snippet_invalid_export的原文档示例值得保留。你不能这样做script module export { greeting }; /script script let message hello; /script {#snippet greeting(name)} p{message} {name}!/p {/snippet}原因是greeting引用了第二个script实例作用域里定义的message而导出 snippet 只能引用script module中声明的东西或其他可导出 snippet。import_svelte_internal_forbidden的消息原文还建议如果是为了绕过 Svelte 的某个限制而导入svelte/internal/*请到官方 Svelte 项目仓库提交 issue 说明使用场景。9. TypeScripttypescript_invalid_featureTypeScript language features like %feature% are not natively supported, and their use is generally discouraged. Outside ofscripttags, these features are not supported. For use withinscripttags, you will need to use a preprocessor to convert it to JavaScript before it gets passed to the Svelte compiler. If you are usingvitePreprocess, make sure to specifically enable preprocessing script tags (vitePreprocess({ script: true }))。要点script外的 TS 特性直接不支持script内需要通过 preprocessor 先转成 JS 再交给编译器使用vitePreprocess时务必显式开启script: true。这些错误在哪里被触发2-analyze 阶段的 visitor 机制从源码结构看以上错误全部在编译的第二阶段分析阶段由 AST visitor 抛出核心文件与职责如下phases/2-analyze/visitors/CallExpression.jsrunes 调用形态校验中心——$bindable/$host/$props/$props.id/$state系列/$effect系列的位置、参数、重复性检查都在这里完成如 L115-L145 对$state/$derived的位置三条件与实参数量校验。phases/2-analyze/visitors/Identifier.js所有以$开头的标识符统一入口负责rune_invalid_name、rune_missing_parentheses、rune_renamed、rune_removed、rune_invalid_computed_property的判定。phases/2-analyze/visitors/EachBlock.jseach 块上下文校验包括把 rune 名误当 each 参数的特判。phases/2-analyze/visitors/PropertyDefinition.js类字段的 state 声明顺序校验state_field_invalid_assignment。phases/2-analyze/visitors/VariableDeclarator.js$props()解构模式逐属性校验props_invalid_pattern针对嵌套属性/计算键。统一出口为 src/compiler/errors.js 中按消息文件分组生成的导出函数全部经InternalCompileError携带位置信息抛出。如何验证与复现compiler-errors 测试套件仓库内置了针对这些错误的完整回归测试每个样例目录包含一个能触发错误的.svelte/.js输入文件和一份_config.js断言错误码 消息原文样例总入口tests/compiler-errors/test.ts样例位于 tests/compiler-errors/samples/ 下目录名与错误场景一一对应如runes-wrong-state-placement、runes-wrong-props-placement-instance、runes-invalid-each-binding、store-global-disallowed、snippet-invalid-export、runes-before-after-update等。代表性断言示例runes-wrong-state-placement/_config.js 断言错误码state_invalid_placement及其完整消息runes-invalid-each-binding/_config.js 断言each_item_invalid_assignment的索引替代写法提示。排查建议遇到某个 script 编译错误时先记下错误码在本文分组表中定位语义再按上文的 visitor 文件路径找到对应校验分支最后到tests/compiler-errors/samples/下找同名样例对照输入文件即可看清该错误的最小复现形式与期望消息。小结这份 script.md 是 Svelte 编译器script编译错误的唯一事实来源52 个错误码覆盖了 runes 使用位置、参数形态、legacy/runes 模式互斥、命名保留字、store 自动订阅作用域、snippet 导出边界、state 字段声明与 TypeScript 预处理等全部script层面约束。每条错误都经 process-messages 模板 生成带svelte.dev/e/错误码链接的CompileError抛出报错信息可直接作为检索与定位的索引。所有校验集中在2-analyze阶段的 AST visitors 中实现配合tests/compiler-errors/samples/下的样例断言形成了「消息定义 → 生成函数 → 校验触发 → 测试固化」的完整闭环。【免费下载链接】svelteweb development for the rest of us项目地址: https://gitcode.com/GitHub_Trending/sv/svelte创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考