12. Пакеты
Пакет — это каталог, содержащий манифест package.archspace и любое количество файлов .arch под ним. Манифест задаёт имя пакета, объявляет его зависимости от других пакетов, указывает на скрипт виджетов и выбирает, какие виды импортируются в область видимости.
name: acme.shopversion: "1.4.0"
dependencies { acme.shared: "../shared" acme.payments: "../packages/payments"}
use * from arch.modulesuse * from arch.kindsuse Database, Cache from acme.sharedЭто полноценный манифест. В этой главе разбирается каждое поле, каждое диагностическое сообщение и правила разрешения.
Анонимные пакеты
Каталог без манифеста — это анонимный пакет: загрузчик обходит все файлы .arch под ним и разрешает их вместе без имени, без зависимостей, без экспорта. Анонимные пакеты предназначены для маленьких черновиков и обратной совместимости. В реальных проектах всегда есть манифест.
Поля
name: (обязательное, ровно один раз)
Идентификатор пакета. Точечная форма принята как соглашение:
name: acme.shop- Минимум один сегмент.
- По соглашению в нижнем регистре; не принуждается.
- Префикс
arch.*зарезервирован за встроенной стандартной библиотекой. Пользовательский пакет вне каталогаstdlib/инструментария, претендующий наarch.<что-либо>, вызывает ошибкуRESERVED_PACKAGE_NAMESPACE.
Дублирующиеся строки name: — ошибка; побеждает первое вхождение, чтобы у инструментария было нечто стабильное.
version: (необязательное, ровно один раз)
version: "1.4.0"Строка произвольной формы. На данный момент только информационная — загрузчик не разбирает, не сравнивает и не ограничивает версии. Поле зарезервировано на будущее; будущий инструментарий будет использовать диапазоны в стиле semver.
widgets: (необязательное, ровно один раз)
Путь (относительно каталога манифеста) к JS/TS-модулю, который регистрирует виджеты-пользовательские элементы через customElements.define():
widgets: "./widgets.js"Просмотрщик динамически импортирует этот скрипт при запуске и переимпортирует его при изменениях файла (после полной перезагрузки страницы — customElements.define срабатывает один раз для имени тега).
Загрузчик проверяет существование файла во время загрузки. Опечатка немедленно вызывает WIDGETS_FILE_NOT_FOUND. Конвейер виджетов — тема Главы 20.
dependencies { ... } (необязательное, не более одного блока)
Отображение имени пакета-зависимости на путь в файловой системе:
dependencies { acme.shared: "../shared" acme.payments: "../../packages/payments"}Для каждой записи загрузчик:
- Разрешает путь относительно каталога манифеста.
- Рекурсивно загружает пакет-зависимость (его собственные зависимости тоже загружаются).
- Проверяет, что
name:загруженного пакета совпадает с именем, объявленным зависящим. Несовпадение вызываетDEP_NAME_MISMATCH.
Ромбовидные зависимости (A→B, A→C, обе →D) загружают D один раз и используют один и тот же экземпляр — загрузчик кэширует по абсолютному пути.
Циклы (A→B, B→A) вызывают DEP_CYCLE для каждого пакета вдоль цикла.
Пакетам arch.* запись не нужна. Загрузчик автоматически разрешает их через встроенную стандартную библиотеку инструментария (настраивается через ARCHLANG_STDLIB).
use … from <pkg> (ноль или более)
Импортирует типы из зависимости или пакета стандартной библиотеки:
// Импорт одного типаuse database from arch.modules
// Несколькоuse service, frontend from arch.modules
// Wildcard — все экспортированные типыuse * from arch.modules
// Переименованиеuse database as managed_db from arch.modules
// Реэкспорт, чтобы потребители ЭТОГО пакета тоже его виделиexport use payments_provider from acme.payments| Форма | Эффект |
|---|---|
use X from p | Импортирует X напрямую. X должен быть отмечен как export в p — иначе USE_TYPE_NOT_EXPORTED. |
use * from p | Импортирует каждый тип, отмеченный как export в p. |
use X as Y from p | Импортирует X под локальным именем Y. |
export use X from p | Реэкспортирует X, чтобы wildcard-импортёры этого пакета тоже получили X. |
Область видимости
use, объявленный вpackage.archspace, имеет область пакета: каждый файл.archв этом пакете может ссылаться на импортированные имена.use, объявленный внутри файла.arch, имеет область файла: только этот файл может на них ссылаться. Ссылки из соседнего файла вызываютKIND_NOT_VISIBLE_IN_FILE.
То же имя, импортированное из того же исходного пакета в нескольких файлах, — нормально; области накапливаются. То же имя из разных исходных пакетов вызывает USE_NAME_COLLISION; переименуйте одно через as.
Анонимные пакеты пропускают проверку export: каждый тип доступен. Как только пакет получает манифест, типы должны быть отмечены export, чтобы быть видимыми зависимым.
Диагностические коды
| Код | Когда |
|---|---|
MANIFEST_PARSE_ERROR | Неправильный синтаксис манифеста |
RESERVED_PACKAGE_NAMESPACE | Пакет вне стандартной библиотеки претендует на префикс arch.* |
DEP_LOAD_FAILED | Путь объявленной зависимости не существует или не разбирается |
DEP_NAME_MISMATCH | name: загруженного пакета отличается от ожидания зависящего |
DEP_CYCLE | Цикл в графе зависимостей пакетов |
STDLIB_NOT_FOUND | Импорт arch.* не удалось разрешить через настроенную стандартную библиотеку |
USE_PACKAGE_NOT_FOUND | use ... from <pkg> ссылается на пакет, которого нет ни в dependencies, ни в arch.* |
USE_TYPE_NOT_FOUND | Названный тип не существует в исходном пакете |
USE_TYPE_NOT_EXPORTED | Тип существует, но не отмечен как export |
USE_NAME_COLLISION | Одно и то же локальное имя импортировано из двух разных исходных пакетов |
WIDGETS_FILE_NOT_FOUND | Путь в widgets: не разрешается в существующий файл |
Проработанные примеры
Минимальный черновик:
name: scratchЧерновой проект, который ни от чего не зависит. Каждый файл .arch в каталоге загружается и может импортировать друг из друга; никакие типы arch.* не видны, потому что use не объявлен.
Стандартный проект со стандартной библиотекой:
name: acme.shopversion: "0.4.0"
use * from arch.modulesuse * from arch.kindsСамая распространённая форма. Подтягивает встроенные виды модулей (service, database, …) и виды интерфейсов взаимодействия (command, query, event, …).
Многопакетный монорепозиторий:
name: acme.appversion: "1.0.0"widgets: "./widgets.js"
dependencies { acme.shared: "../shared" acme.payments: "../packages/payments"}
use * from arch.modulesuse Database, Cache from acme.sharedexport use payments_provider from acme.paymentsОбъявляет зависимости, импорты для всего проекта плюс реэкспорт, чтобы всё, что зависит от acme.app, также подхватывало payments_provider.
Библиотечный пакет:
name: acme.sharedversion: "2.1.0"Библиотека определяет типы в своих файлах .arch и помечает публичные через export. Потребители подключают их по имени через use; неэкспортированные типы остаются внутренними.
Структура файлов
my-project/├── package.archspace # этот файл├── widgets.js # регистрации пользовательских элементов (необязательно)├── orders.arch # типы / модули / процессы / проекции├── kinds.arch # типы, локальные для проекта└── packages/ └── shared/ ├── package.archspace # вложенный пакет — СВОЯ единица └── lib.archВложенный package.archspace — это жёсткая граница: обход файлов родителя останавливается на каталоге вложенного манифеста, а вложенный пакет разрешается как собственная единица, когда на него явно ссылаются через dependencies или он автоматически загружается как arch.*.
Резюме
- В каждом проекте есть манифест
package.archspaceв корне. - Поля:
name:(обязательное),version:,widgets:,dependencies { },use … from …. arch.*зарезервировано за стандартной библиотекой и разрешается автоматически; пользовательским зависимостям нужен путь.useв манифесте имеет область пакета;useв файле.archимеет область файла.- Ромбовидные зависимости разделяют один экземпляр; циклы вызывают
DEP_CYCLE.
Что дальше
Глава 13: Стабильные идентификаторы → — идентичность, переживающая переименования, и где идентификаторы применимы, а где нет.