A technology agonistic, semantically versioned, file-oriented dependency manager.
- Simplified tooling for projects spanning multiple technologies.
- Improve code quality through across touched ecosystems.
- Reduce prevalence of duplicated code.
- Minimise risk of breaking changes between non-major releases.
Module resolution occurs during dependency installation through the use of path rewrites in dependencies.
Treatment of duplicate dependencies is subject to the technologies within. As an example, PHP would only allow one version of a dependency to be installed citing language contraints, while JavaScript will quite happily handle extras.
To discourage the special kind of dependency hell that is having 10+ versions of the same dependency, duplicates will prompt a warning.
To help prevent vulnerable or old versions of dependencies being used in production code, development dependencies will not be considered in the initial dependency graph resolution. Development dependency resolution is then conducted with the already resolved dependencies acting as semver pinned versions.
This offset will cause dependency resolution failures in projects with dependencies that are unable to handle duplicates.
The directory structure has been designed to be consistent and predictable. The below chart illustrates this.
.
+-- vendor
| +-- super
| | +-- d (3.2.7)
| | | +-- sourcetype-pub.json
| | | +-- super.d.ts
| | | +-- super.js
| | | +-- super.less
| | +-- 0.2.7
| | +-- sourcetype-pub.json
| | +-- super.js
| | +-- super.sass
| +-- company-brand-kit
| | +-- 1.1.0-rc.1
| | +-- sourcetype-pub.json
| | +-- svg
| | | +-- square.svg
| | | +-- banner.svg
| | +-- png
| | +-- square.png
| | +-- banner.png
| +-- bin-serve
| +-- d (playground)
| +-- sourcetype-pub.json
| +-- serve.exe
| +-- serve
+-- sourcetype.json
+-- sourcetype-lock.json
All dependencies are held at the same level in the directory structure to ease auditing effort and avoid path length cap issues in certain tools.
Direct dependencies are uniquely marked as such with d
to permit easy usage of dependency content in a predictable fashion. Version information via folder name is lost by doing this however there is no significant disadvantage as usage of a dependency requires that its documentation be read, which in turn requires the version to verify.
Dependencies on certain external entities such as the OS or runtime can be declared to ensure 2 things.
- The execution environment is compatible
- All resolved dependencies should work across all declared environments
Dependencies that declare requirements but have no rule to check against will result in a preference for a combination that produces the widest compatibility range. When this occurs a warning will be produced along with some recommendations to ensure that all intended environments are supported.
In semantic versioning the 0.x.x version range is commonly used for the release of developmental code. This however does to work so well once 1.0 has been reached making effective open development tricky. Furthermore, the tagging of versions at these volitate times is counterproductive and many send the wrong message if the intent is simply to test something that requires publishing.
The solution proposed is to have a playground to which releases can be deployed carelessly without risk. A special utility that dependents can point to during development to test fixes, new feature, etc. Depending on the success of this feature, it could be extended much further and play a key part in maintaining stability of the ecosystem.
Publishing of projects depending on playgrounds will be blocked to prevent misuse.
The file used within projects.
{
"$schema": "https://schema.typesource.org/sourcetype/1.0.0",
"name": "sourcetype",
"version": "1.0.0",
"license": "LICENSE.md",
"addToPath": {
"linux": "bin/linux/",
"linux-x64": "bin/linux-x64/",
"win10": "bin/win10/"
},
"lifecycleHooks": {
"preInstall": "",
"postInstall": "",
"preUpdate": "",
"postUpdate": "",
},
"commands": {
"build": "dotnet build"
},
"external": {
"coolConfig": {}
},
"dependencies": {
"*": {
"cool-cli-lib": "7.54.12"
},
"dev": {
"dotnetcore": "3.0.0"
}
},
// TODO These are used in dependency resolution as well, so only dependencies that also support linux will be used.
// TODO Omitting any section automatically means "everything"
"environments": {
"*": {
"os": {
"listedOnly": true,
"supported": {
"win": "^8.1 || >=10"
},
"unsupported": {
"linux": ">=1.6.4"
}
}
},
"dev": {
"runtimes": {
"node": "^10.0.0"
}
}
}
}
The file used to ensure a measure of consistency across development environments and cache the dependency resolution result along with any transformations to be performed to paths.
{
"$schema": "https://schema.typesource.org/sourcetype-lock/1.0.0",
"dependencies": {
"*": {
"cool-cli-lib": [
{
"version": "7.54.12",
"integrity-hash": {
"original": "...",
"current": "..."
},
"changes": {
"./foo/bar.ts": [
{
"start": 123,
"end": 124,
"offset": 3,
"content": "5.0.3"
}
]
},
"dependsOn": {
"cool-lib": "5.0.3"
}
}
],
"cool-lib": [
{
"version": "5.0.3",
"integrity-hash": {
"current": "..."
}
}
]
},
"dev": {
"dotnetcore": [
{
"version": "3.0.0",
"integrity-hash": {
"current": "..."
}
}
]
}
},
// TODO This should represent the requirements of dependencies as well
"environments": {
"*": {
"os": {
"listedOnly": true,
"supported": {
"win": "^8.1 || >=10"
},
"unsupported": {
"linux": ">=1.6.4"
}
}
},
"dev": {
"runtimes": {
"node": "^10.0.0"
}
}
}
}
The file used for managing a dependency.
{
"$schema": "https://schema.typesource.org/sourcetype-pub/1.0.0",
"publishedWith": "SourceType CLI 1.25.2",
"version": "3.64.1",
"dependencies": {
"cool-dep": "^3.8.236",
"cooler-dep": "^3.1.6"
},
"addToPath": {
"linux": "bin/linux/",
"macos": "bin/macos/",
"win": "bin/win/"
},
"environments": {
"os": {
"win": "^8.1 || >=10",
"linux": ">=1.6.4"
}
}
}
Scripts can be manually run via;
run
to open a terminalrun -r "cmd"
to execute the provided stringrun "script-name"
to execute predefined snippets
For convenience various hooks for various in-box commands exist. To reduce the risk of rouge scripts permissions available to each hook will vary according to their implicit permissions. Some examples of implicit permissions are;
test
implicitly grants reading, writing, and execution permissionsinstall
implicitly grants almost nothing
There are always cases where the granted implicit permissions may not be enough for a genuine use case, so to address this permission requirements can be declared. If these permission requirements are not impilictly met, the user will be prompted to either grant them and continue or decline and stop.
Any command executed through the package manager recieves a contextual PATH
variable consisting exclusively of direct dependencies, and be run through a Hosted PowerShell Core instance to permit greater consistency across platforms. To ensure that dependencies are always satisfied PATH
will change along with the context.
Given the risk of tooling mismatches global dependencies and the challenges around dealing with them, their support is outside of the scope of this initial draft.