Path 字面量
p"./data.json" 是 Path 类型的字面量,与普通字符串不同,没有隐式转换。创建 Path 只确定逻辑资源身份,不读取文件、不检查存在性,也不自动发布资源。
fn identity(source: Path) -> Path { source }
fn view(source: Path = p"./default.json") -> BlockContent {
// Path 可以转发给其他 notc 函数;不会随调用位置重新解释。
let input = identity(source);
[静态正文]
}
#import "../views/lib.notc" as view;
#view.view(source: p"./data.json")
相对位置
文档中写下的 Path 相对该文档所在目录。
默认参数和函数体中的 Path 相对定义模块所在目录。
以
/开头的字面量相对宿主指定的逻辑资源根,不是操作系统根。.、..和多余的/按词法规范化;不访问文件系统,不跟随符号链接。不允许通过
..逃出所属逻辑根。相同规范身份的 Path 可以用==和!=比较。
Path 可以出现在参数、返回值、数组、字典和属性注解中;注解和默认值仍只接受字面量,不执行任意表达式。
Path 字面量写作紧邻的单行 p"...",使用普通字符串转义。路径使用 /,不接受反斜杠、控制字符、空路径、URL 或盘符路径。p 本身不是保留字。
编译与宿主边界
Path 字面量在 Resolve 阶段按源文件和逻辑根绑定,后续转发保留该身份。
独立 Pipeline 默认使用虚拟逻辑根 /,不使用进程工作目录。宿主可以用 Context.with_resource_root 指定文档逻辑根,用 Pipeline.compile_module_with_root 指定模块逻辑根;Vault 使用其 Resources 的根。独立内容片段可通过 Pipeline.analyze_content_literal_at 明确提供来源,方言占位路径只用于选择语法。生成页面只需提供逻辑路径和 Markup 源码,不必落盘。
Path 不会自动变成 HTML 属性、组件参数或公开 URL;这类导出会产生诊断。IR/调试 JSON 使用显式的 Path 标记,保留资源身份。
本地 embed
embed 是显式的资源消费边界,接受 Path 或 URL 字符串:
#embed(p"./images/chart.svg", description: "图表", fragment: "detail")
#embed(p"./file?name#part%.svg", query: "raw=1", fragment: "detail")
#embed("https://example.com/chart.svg#detail")
Path 中的 ?、# 和 % 都属于文件名,不拆分也不做 URL 解码。query 是不带 ? 的 URL 查询文本,不能包含控制字符或 #;fragment 是不带 # 的语义片段,发布时编码一次。
也可使用图片语法  或字符串形式 #embed("image.svg?raw=1#detail")。本地字符串在 embed 构造边界按该操作所在的源文件绑定为 Path;URL 路径和 fragment 百分号解码一次,query 保留 URL 文本。远程 URL 保持字符串,不作为本地资源。同一 query/fragment 不能同时在 URL 和显式参数中提供。转发已经绑定的 Path 不重新解析;转发普通本地字符串则在最终 embed 操作处解释,因此可复用函数推荐使用 Path 参数和 Path 默认值。
ResourceTarget 是接受 Path 或 String 的输入类型,不是另一种运行期 Value,也不提供两者之间的转换。资源存在性、Vault/mount 授权和发布 URL 仍由宿主负责。独立 Renderer 需要 with_resource_resolver 选择本地资源的公开 URL;没有映射时诊断并省略链接,不直接输出 Path。
链接与路径派生
link 是泛型函数,其 where 约束只接受 Path 或 String。Path 保留创建位置绑定的资源身份,经组件转发也不重新解释;宿主将文档映射到页面路由,将其他资源映射到发布 URL。缺少发布映射时诊断并省略 href,不回退到路径字符串。String 保留 URL 链接规则。
#link(p"./guide.not", fragment: "intro")[指南]
#link(p"./model.glb")[下载模型]
#link("https://example.com")[外部网站]
query 与 fragment 是独立字段;Path 文件名中的 ?、#、% 不拆分。
Path.join(relative: String) -> Path 将接收者视为目录,追加非空相对路径并按词法规范化。结果保留同一个逻辑根,拒绝绝对路径、URL 及逃出根的路径;不读取文件,也不将 Path 转为 String。
fn download(directory: Path, name: String) -> InlineContent {
[#link(directory.join(name + ".glb"))[下载模型]]
}
调用者传入 p"." 可明确选择调用文档所在目录。默认参数中的 p"." 仍绑定定义模块目录,不代表调用者目录。
读取文本
read_text(source: Path) -> String 读取 UTF-8 文本,可在 notc 函数体中使用:
fn from_file(source: Path = p"./intro.txt") -> BlockContent {
let body = read_text(source);
[ #text(text: body) ]
}
#import "views/lib.notc" as view;
#view.from_file(p"./private/intro.txt")
在 Markup 中,通过上例的内容函数读取文件。读取发生在实际执行时;check 与 LSP 不执行读取,comptime 中禁止读取。
结果是普通 String,不会再次当作 Markup 解析;text 显式将其变成正文,HTML 正常转义。缺失、拒绝、目录、超限和非法 UTF-8 都在实际读取调用的位置诊断,并沿既有内容边界恢复。
Vault 默认允许项目根内的数据读取;外部模块和 Book mounts 的数据需要单独授权。
同一批次保留每个输入首次读到的字节或错误,包括缺失文件。后续读取使用缓存,不随磁盘修改而变化;文件更新、修复或创建后以新 Vault 开始下一轮。缓存命中仍计费、登记依赖并检查读取授权。
独立 Pipeline 可使用 evaluatewithinputs 接收宿主输入;不带 inputs 的 evaluate 可以执行无读取的文档,实际读取时报告能力不可用。浏览器按 NeedResources 准备字节或明确失败,再以新视图重试,不发布部分结果。接口、预算与一致性规则见 资源输入与求值。
JSON/TOML 解码与受检访问见 Data。