@git.zone/tspublish
Plan and prepare independently installable TypeScript packages from one repository.
Each component has its own dependency list and package metadata; components share
the repository version. TsBuild owns compilation. tspublish does not publish:
gitzone release prepares all packages through tspublish before packing,
journaling and publishing any of them.
Issue Reporting and Security
For reporting bugs, issues, or security vulnerabilities, please visit community.foss.global/. This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a code.foss.global/ account to submit Pull Requests directly.
Install
pnpm install --save-dev @git.zone/tspublish @git.zone/tsbuild
Repository structure
Keep the repository manifest private when it holds dependencies for the entire toolbox. Each named module becomes a separate public package with a filtered dependency manifest.
package.json
tsconfig.json
license.md
ts_core/
index.ts
readme.md
tspublish.json
ts_adapter/
index.ts
readme.md
tspublish.json
The repository's package.json supplies the shared version and external dependency
versions. Internal package dependencies use the exact shared version, even when an
older published version appears in the root manifest. Unknown external dependencies
fail validation instead of being assigned the repository version.
Each package requires its own readme.md. The root license is copied into every
prepared package. license.md is preferred; existing repositories using license
remain supported.
Module configuration
{
"name": "@example/adapter",
"order": 2,
"description": "Optional provider adapter for the example toolbox.",
"dependencies": ["@example/core", "external-sdk"],
"registries": ["useBase"],
"bin": [],
"engines": { "node": ">=24" },
"sideEffects": false,
"include": ["third-party-notices.md"]
}
Supported fields:
| Field | Meaning |
|---|---|
name |
Published npm package name. Unnamed descriptors remain available to TsBuild for ordering only. |
order |
Ordering preference among modules whose dependencies are ready. Dependencies always precede consumers. |
dependencies |
Required package names, resolved from sibling modules or root production dependencies. |
optionalDependencies |
Optional dependency names, resolved by the same rules. |
peerDependencies |
Explicit peer version ranges. |
peerDependenciesMeta |
Optional-peer metadata. |
description |
Package description; defaults to the root description. |
engines |
Runtime requirements; defaults to the root engines. |
os, cpu, libc |
Nonempty npm platform-selector arrays. Entries may use ! to exclude a platform; duplicate entries are rejected. Each module declares its own selectors. |
publishConfig.executableFiles |
Exact package-relative files that pnpm must pack with executable permissions. No leading ./, traversal, globs, duplicates, symlinks or directories. Every path must already be included in this package. Other publishConfig keys are rejected. |
sideEffects |
Standard package side-effect declaration. |
folders |
Additional ts_* folders owned by this package. A folder may have only one owner. |
exports |
Explicit subpath/conditional exports into the package's compiled folders. Defaults to typed root exports. |
include |
Exact repository-relative asset files or directories, including applicable notices. No globs or component source/compiled folders; use folders to declare source ownership. |
externalRuntimeAssets |
Exact repository-relative files or directories executed by another runtime, such as Deno source. Copied without npm import validation; the package owner must qualify that runtime and its dependency graph. The same filesystem and folder-ownership restrictions as include apply. Asset declarations cannot overlap. |
bin |
Executable names using the module's exported runCli() entrypoint. Wrappers are generated locally. |
registries |
Destination intent, returned in the plan for the release coordinator to validate. gitzone release requires ["useBase"]. |
Root author, license, repository, homepage, bugs, keywords and funding metadata are preserved. The private repository flag, root scripts, unrelated dependencies and development dependencies are not copied into prepared packages.
Legacy main and types fields are derived from the root export's node,
import, default, and types conditions when available. They never point at
an unrelated default entrypoint when root exports are customized.
Native packages can declare "os": ["linux"], "cpu": ["x64"],
"libc": ["glibc"] and
"publishConfig": { "executableFiles": ["dist_ts_native/native"] }.
Place the built payload in that module's compiled folder before preparation and
include its applicable notices. Preparation preserves the payload's bytes and
source permissions; pnpm applies executable permissions when packing it. A parent
module can list these platform packages in optionalDependencies, which uses the
same exact sibling versions and dependency ordering as required dependencies.
A wrapper that launches Deno can declare "include": ["deno.json", "deno.lock"]
and "externalRuntimeAssets": ["mod.ts", "deno_src"]. These files are copied
unchanged. Validate the Deno configuration, locked dependencies and execution
separately before release. This declaration does not add npm dependencies or allow
component ts*/dist_ts* folders to bypass npm import checks. Node source imports
into these external assets are not admitted by the declaration.
For a subpath, include the additional source folder and declare both runtime and type entrypoints:
{
"name": "@example/core",
"order": 1,
"dependencies": [],
"registries": ["useBase"],
"folders": ["ts_migration"],
"exports": {
".": {
"types": "./dist_ts_core/index.d.ts",
"import": "./dist_ts_core/index.js"
},
"./migration": {
"types": "./dist_ts_migration/index.d.ts",
"import": "./dist_ts_migration/index.js"
}
}
}
Imports and local development
Import sibling components through their published package names and declare them
in dependencies. Configure TypeScript paths to their source entrypoints within
the same repository; GitZone's TypeScript formatter discovers these mappings from
tspublish.json. This requires no workspace symlinks or local package dependencies.
Run the normal repository build before preparation:
pnpm build
Preparation requires the matching dist_ts* outputs and verifies every export
target. It inspects literal imports in Node sources, declarations and ordinary
include assets, and rejects
undeclared dependencies and relative imports outside the component. Computed
runtime imports remain the responsibility of the module author. Built-in Node
modules are allowed. Browser compatibility must still be tested by each component.
Source folders, compiled folders and explicit assets are copied separately for
each package. Symlinks, credential files and local dependency directories are
rejected, including inside externalRuntimeAssets. Preparation does not bundle
dependency implementations.
A package leaves out the tspublish.json and the tsconfig.json of every folder
it owns. A folder tsconfig.json sets that folder's TypeScript environment for
TsBuild, for example "lib": ["webworker", "esnext"] for a service worker; see
"Folder tsconfig.json" in the @git.zone/tsbuild readme. The package ships what
TsBuild compiled with those settings. The file itself extends the repository's
tsconfig.json, which no package contains, so TypeScript could not read it inside
the package (TS5083). Only the tsconfig.json directly inside an owned folder is
left out, the one TsBuild reads.
Plan and prepare
import { TsPublish } from '@git.zone/tspublish';
const publisher = new TsPublish();
const plan = await publisher.plan(process.cwd());
// plan.modules is in deterministic dependency order; order equals its array index.
const packages = await publisher.prepare(
process.cwd(),
'/absolute/existing-parent/new-package-directory',
);
// Each result contains folder, folders, name, version, order, manifest,
// include, licenseFile, registries and its prepared directory.
plan() reads manifests and license availability without writes, builds or network
calls. It rejects duplicate package names, dependency cycles, unknown external
dependency versions, local dependency links and invalid package paths. A
tspublish.json that is not valid JSON fails with Invalid JSON in <path>: <reason>,
and one that is not a JSON object with Invalid <path>: expected a JSON object.,
the wording TsBuild uses for the same file. The repository's package.json fails the
same way.
A repository in which no descriptor names a package, whether it has only
order-only descriptors or no ts* folders at all, plans as
{ schemaVersion: 1, version, modules: [] } with the root package.json version.
That plan reads no license, so a release coordinator can plan any versioned npm
repository and decide from modules.length whether it has packages to release.
Discovery is silent: getModuleSubDirs(), plan() and prepare() write nothing to
the console, because build tools and release coordinators call them as a library.
prepare() plans again and materializes the already-built packages. The output
directory must not exist; its parent must already exist at a canonical path. Output
cannot be nested inside an input folder. Preparation removes its own incomplete
output on failure and never overwrites a prior result. The caller owns successful
prepared directories and their cleanup.
Neither method installs dependencies, fetches templates, contacts registries, publishes packages or terminates the process.
The CLI exposes the same operations and writes only their JSON result to stdout:
pnpm exec tspublish plan
pnpm exec tspublish prepare /absolute/existing-parent/new-package-directory
Any other invocation, including a bare tspublish, prints usage to stderr and
exits with status 1. A failed plan or prepare prints its message to stderr,
without a stack trace, leaves stdout empty and exits with status 1. The exported
runCli() rejects with that error; the binary reports it.
A release coordinator should validate every module's destination intent, prepare all modules, pack every package, record the complete immutable artifact set, and then publish in the returned dependency order. Publication recovery must reuse those retained tarballs rather than rebuilding or preparing again.
Publishing
Publish tspublish modules with gitzone release from @git.zone/cli 7.0 or later.
It publishes every named module inside its journaled release, in plan order and
before the root package, to the registries in release.targets.npm; see
"Publishing independently installable components" in the @git.zone/cli readme.
Version 2.0.0 removed the standalone publisher, TsPublish.publish() and the bare
tspublish command. Remove package scripts that ran it; gitzone release refuses
to release beside one.
Development
pnpm build
pnpm test
pnpm exec tsbuild check 'test/**/*'
Packaging tests use disposable directories and do not publish anything.
License and Legal Information
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the license.md file.
Please note: The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
Trademarks
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
Company Information
Task Venture Capital GmbH Registered at District Court Bremen HRB 35230 HB, Germany
For any legal inquiries or further information, please contact us via email at hello@task.vc.
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.