Skip to content
FutureBlitz

    Install

    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.toml
    [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:

    LICENSE
    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.
    README.md
    # slug
    
    Turns titles into URL slugs: `Slug("Hello, World!")` is `hello-world`.

    The library’s code is one module, slug.fb:

    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 Function

    Tests 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.

    tests/slug_test.fb
    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 Function
    Output
         Running 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 fileWhat it is
    licensean SPDX expression, like "MIT" or "MIT OR BSD-3-Clause"
    descriptionone line, of 300 characters at most
    the license’s textLICENSE, LICENSE.md, LICENSE.txt, or COPYING, or the file license_file names
    a readmeREADME.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
    Output
       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 names

    Publishing

    fb login

    fb 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
    Output
       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.0

    To 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
    Output
          Yanked slug v1.0.0
        Unyanked slug v1.0.0

    A 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 publish needs license and description in project.toml, a license file, and a readme.
    • fb publish --dry-run checks 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.

    Report an issue with this page or with FutureBlitz on GitHub.