# 多文件应用包

应用包保存完整的文件集合。复杂应用可以直接拆分页面、数据逻辑和样式，不需要把所有代码放在一个 `render` 函数中。

```text
app.json
index.js
pages/
  home.js
  detail.js
lib/
  todo.js
styles/
  app.css
```

## app.json

```json file="app.json"
{
  "name": "待办",
  "entry": "index.js"
}
```

当前运行时使用 `entry` 选择入口模块。`name` 是基础描述，桌面显示名称以创建应用时的 `name` 参数为准。旧 `permissions` 字段没有授权语义。新格式可以通过以下声明申请已登记能力；适用平台、主题模式和后台任务仍未开放。

```json
{
  "formatVersion": 1,
  "sdk": { "major": 1, "minMinor": 0 },
  "entry": "index.js",
  "capabilities": {
    "required": ["data.read"],
    "optional": ["clipboard.write"]
  }
}
```

必需能力未知或不可用时拒绝启动；可选能力通过 `platform.can` 检查。`ui`、`app.close` 是基础能力。旧包默认申请数据读写及可选剪贴板能力，但仍须经过用户首次授权。能力协议正在跨端验证，尚未冻结。

省略配置时平台自动生成基础 `app.json`。推荐始终明确提供有效的配置和入口，避免依赖入口回退行为。

## 模块引用

支持包内相对路径的静态 `import … from` 和 `export … from`。建议始终写出 `.js` 扩展名，不使用裸模块名、CDN、绝对路径或动态 `import()`。

```js file="lib/todo.js"
export async function loadOpen(platform, datasetId) {
  return platform.data.query(datasetId, {
    where: { done: false },
    orderBy: { field: "created_at", dir: "desc" },
    limit: 50,
  });
}
```

```js file="index.js"
import { loadOpen } from "./lib/todo.js";

export default async function render(root, platform) {
  const todos = await platform.data.get("todo");
  const rows = await loadOpen(platform, todos.id);
  root.replaceChildren(platform.ui.title("未完成 " + rows.length));
}
```

当前模块加载器只处理带 `from` 的相对路径语法。不要使用 `import "./setup.js"` 这种副作用导入。`.json` 可作为包文件保存，但不能直接作为 JavaScript 模块导入；配置数据可放入导出对象的 `.js` 模块。

## CSS

包内所有 `.css` 文件由宿主自动注入，不需要 `import "./styles/app.css"`。使用[设计变量和布局类](/doc/style)，避免相互覆盖的多份样式，不依赖 CSS 文件注入顺序。

## 文件限制

| 项目 | 当前限制 |
| --- | --- |
| 文件数量 | 最多 24 个，包含自动生成的 `app.json`。 |
| 扩展名 | `.js`、`.json`、`.css`。 |
| 路径 | 相对路径，以英文字母或数字开始，允许字母、数字、下划线、连字符、点和目录分隔符。 |
| 路径约束 | 不能以 `/` 开始，不能包含 `..`。模块中的 `../` 引用可用于访问包内上级目录，但文件名自身不能包含 `..`。 |
| 单文件大小 | 当前按 JavaScript 字符串长度限制为 120,000，并非按 UTF-8 字节数计算。 |

上传新包时会创建完整的不可变源码快照，并切换当前版本引用。运行中的实例继续使用已加载版本；新打开的实例使用新版本。请阅读[安装与更新](/doc/install)后再提交更新。
