Modules and packages, 6 of 6
Publishing a package
Make a library others can use, document and test it, and publish it to the registry with fb publish.
What you’ll build
A library package, slug, with one function that turns a title like “Hello, World!” into
hello-world, the form URLs use. You’ll document it, test it, describe it in project.toml,
give it a license file and a readme, check what would be uploaded, and publish it, so that any
program can fb add slug.
A library
A library is a package with type = "library" and no main file. Its Public declarations are what
its users get. The //! lines at the top of a file document the module, and the /// lines
before a declaration document it. Each code block under a # Examples heading in a doc comment
is a test too, so the documentation’s examples always work.
[project]
name = "slug"
version = "1.0.0"
type = "library"
license = "MIT"
description = "Turns titles into URL slugs."A package that’s published also has the license’s full text in a file, LICENSE, and a readme,
README.md, which the registry shows on the package’s page:
Copyright (c) the slug authors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.# slug
Turns titles into URL slugs: `Slug("Hello, World!")` is `hello-world`.The library’s code is one module, slug.fb:
//! Turns titles into slugs, the lowercase, dash-separated words that URLs use.
Import std.core
Import std.text
/// The slug of `title`: its letters and digits, lowercase, with one `-` between each run of them.
///
/// # Examples
///
/// ```
/// Expect(Slug("Hello, World!") == "hello-world")
/// ```
Public Function Slug(title: String): String
Local out = StringBuilder()
Local gap = False
For c In title.Chars
If c.IsLetter() || c.IsDigit()
If gap && out.Length > 0 Then out.Append("-")
out.Append(c.ToLower())
gap = False
Else
gap = True
End If
End For
Return out.ToString()
End FunctionTests from outside
The files in a package’s tests/ folder are tests that use it as another package would, through
its Public names only. fb test runs them, with the examples in the doc comments.
Import std.core
Import std.test
Import slug
@Test
Function KeepsDigits()
Expect(Slug("Top 10 Lists") == "top-10-lists")
End Function
@Test
Function DropsEdges()
Expect(Slug(" --Draft-- ") == "draft")
End FunctionRunning 3 tests test slug.slug.Slug (example) ... ok test slug.tests.slug_test.DropsEdges ... ok test slug.tests.slug_test.KeepsDigits ... ok test result: ok. 3 passed; 0 failed; 0 skipped; 0 filtered out; finished in 0.02s
Run every test before publishing: a version, once published, can’t be changed.
What the registry shows
The registry shows a package from what its project.toml says and the files beside it.
fb publish needs these:
| Key or file | What it is |
|---|---|
license | an SPDX expression, like "MIT" or "MIT OR BSD-3-Clause" |
description | one line, of 300 characters at most |
| the license’s text | LICENSE, LICENSE.md, LICENSE.txt, or COPYING, or the file license_file names |
| a readme | README.md, README, or README.txt, or the file readme names, in UTF-8 |
The rest is optional: repository, homepage, and documentation, each an https:// URL, and
keywords, up to five words that search finds the package by, each of lowercase letters, digits,
and -.
[project]
name = "slug"
version = "1.0.0"
type = "library"
license = "MIT"
description = "Turns titles into URL slugs."
repository = "https://github.com/example/slug"
keywords = ["url", "text"]A package’s name is what Import names, and it’s the registry’s too: lowercase letters, digits,
and _, starting with a letter.
Checking before publishing
--dry-run builds the archive into build/package/ and checks what can be checked without the
registry: that the package is a library, that it has a description, a license that’s an SPDX
expression, a license file, and a readme, that its dependencies all come from a registry, none
from a folder, and that it’s within the size limits. Then it lists the archive’s files, and stops
without uploading anything:
fb publish --dry-run Packaging slug v1.0.0 (C:/Users/ada/slug)
Packaged slug-1.0.0.fbpkg: 4 files, 1.3 KB
slug-1.0.0.fbpkg LICENSE
slug-1.0.0.fbpkg README.md
slug-1.0.0.fbpkg project.toml
slug-1.0.0.fbpkg slug.fb
Finished the dry run of slug v1.0.0; nothing was published to https://index.futureblitz.dev/The archive holds project.toml, the license and readme, and the source, but not build/,
tests/, fb.lock, or hidden files. Without LICENSE, the dry run stops at the first check it
fails:
error: fb publish needs the license's text in the package's folder: LICENSE, LICENSE.md, LICENSE.txt, or COPYING, or the file [project]'s license_file namesPublishing
fb loginfb login signs you in to the registry through its page in your browser, and stores the token it
gets in your OS’s credential store. Then fb publish runs the same checks as --dry-run, makes
the package’s docs with fb doc, checks with the registry that the version is new, and uploads
the archive and the docs, which the package’s page on the registry shows:
fb publish Packaging slug v1.0.0 (C:/Users/ada/slug)
Packaged slug-1.0.0.fbpkg: 4 files, 1.3 KB
Publishing slug v1.0.0 to https://index.futureblitz.dev/
Published slug v1.0.0To publish a change, raise version in project.toml and publish again: 1.0.1 for a fix, 1.1.0
for something new that doesn’t break existing code, 2.0.0 for a change that does. A version that
shouldn’t be used, like one with a bad bug, can be yanked, and --undo takes the yank back:
fb yank slug@1.0.0
fb yank slug@1.0.0 --undo Yanked slug v1.0.0
Unyanked slug v1.0.0A yanked version stays downloadable for programs whose fb.lock already has it, and no new resolution picks it.
What you learned
- A library is a package with
type = "library", whose Public declarations are its API. ///and//!comments document it, and their examples run as tests.fb publishneedslicenseanddescriptioninproject.toml, a license file, and a readme.fb publish --dry-runchecks the package and lists what would be uploaded.- Published versions never change; a new version is a new publish, and a bad one is yanked.
The rules are in the language reference.
Report an issue with this page or with FutureBlitz on GitHub.