npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

lib-address

v0.6.0

Published

A library to format and validate addresses based on Google's open-source address metadata.

Downloads

332

Readme

LibAddress

GitHub GitHub Workflow Status (with event) npm npm bundle size codecov

Intro

A tiny library for building forms, parsing and formatting addresses based on Google's AddessValidationMetadata.

Features

  • 🧙‍♂️  Full static typesafety & autocompletion.
  • 🌍  Supports countries listed in AddessValidationMetadata.
  • 🪶  Tiny - 2.5kb gzipped and zero dependencies.
  • 🛡️  Zod support (optional).
  • 📖  Fully documented.
  • 🧪  Fully tested.
  • 🎨  Supports multiple languages and latinised equivalents.
  • 📦  Supports tree-shaking.

Motivation

When building applications for the web, it is often necessary to collect address information from users or companies. However, every country has differents ways of handling addresses.

Whether it is the zip (or postal code) format, the need for a state or province, or the way to format the address itself.

This library aims to provide a simple API for parsing and formatting addresses and interfaces nicely with Zod.

The library is written in TypeScript and is fully typed.

Table of Contents

Install

Using npm:

$ npm install lib-address

Using yarn:

$ yarn add lib-address

Using pnpm:

$ pnpm add lib-address

Using bun:

$ bun install lib-address

Once the package is installed, you can import the library using import or require approach:

import { formatAddress, isAddressValid } from "lib-address";

When the package is used in a browser, you need to include the country data you want to use.

import { registerCountry } from "lib-address";
import CA from "lib-address/countries/CA.json";
import US from "lib-address/countries/US.json";

registerCountry(CA);
registerCountry(US);

This is done in order to reduce the size of the package and to allow the user to only include the countries they need. When using it in a Node.js environment, all countries are included by default.

Usage

import { isAddressValid } from "lib-address";

isAddressValid({
  country: "FR",
  addressLine1: "1 rue de Rivoli",
  city: "Paris",
  zip: "75001",
});

API

Registry

registerCountry

Used in browser environment to register a country. Countries needs to be registered before using the library. This is done in order to reduce the size of the package and to allow the user to only include the countries they need.

One recommended way to do this is to create a file that will register all the countries you need and import it in your application.

// address.ts
import { registerCountry } from "lib-address";
import US from "lib-address/countries/US.json";

registerCount(US);

export { formatAddress, isAddressValid } from "lib-address";

getCountryData

Returns data of a registered country. Mostly used internally, prefer the use of helpers to access country data.

import { getCountryData } from "lib-address";

const countryData = getCountryData("US");

getRegisteredCountries

Returns a list of registered countries. Useful to build a country select form and only display countries that you have registered.

import { getRegisteredCountries } from "lib-address";

const countries = getRegisteredCountries();

export function CountrySelect() {
  return (
    <select>
      {countries.map((country) => (
        <option key={country.value} value={country.value}>
          {country.name}
        </option>
      ))}
    </select>
  );
}

Validator

isValidCountryCode

Checks if a string is a valid country code (ISO 3166-1 alpha-2)

import { isValidCountryCode } from "lib-address";

isValidCountryCode("US"); // true
isValidCountryCode("USA"); // false

isValidSubdivisionCode

Checks if a string is a valid subdivision code (ISO 3166-2)

[!NOTE The country needs to be registered in order to validate the subdivision code.

import { isValidSubdivisionCode } from "lib-address";

isValidSubdivisionCode("US", "CA"); // true
isValidSubdivisionCode("US", "ZZ"); // false

validateAddress

Validates an address object against country rules. Throws a MissingFieldsError if any required fields are missing. Throws a InvalidStateError if the state code is invalid (if applicable). Throws a InvalidZipError if the zip code is invalid (if applicable). Throws a InvalidZipSubRegionError if the zip code is invalid for the subdivision (if applicable).

import { validateAddress } from "lib-address";
validateAddress({
  country: "US",
  addressLine1: "1 Infinite Loop",
  city: "Cupertino",
  subdivision: "CA",
  zip: "95014",
}); // OK
validateAddress({
  country: "US",
  addressLine1: "1 Infinite Loop",
  city: "Cupertino",
  subdivision: "CA",
  zip: "98014", // <- InvalidZip for US-CA
}); // InvalidZipSubRegionError
validateAddress({
  country: "US",
  addressLine1: "1 Infinite Loop",
  city: "Cupertino",
  subdivision: "CA",
  zip: "8014", // <- InvalidZip for US
}); // InvalidZipError
validateAddress({
  country: "US",
  addressLine1: "1 Infinite Loop",
  city: "Cupertino",
  subdivision: "ZZ", // <- InvalidState for US
  zip: "95014",
}); // InvalidStateError
validateAddress({
  // Missing Zip and State
  country: "US",
  addressLine1: "1 Infinite Loop",
  city: "Cupertino",
}); // MissingFieldsError

isAddressValid

Checks if an address object is valid against country rules. Returns true if the address is valid, false otherwise.

import { isAddressValid } from "lib-address";
isAddressValid({
  country: "US",
  addressLine1: "1 Infinite Loop",
  city: "Cupertino",
  subdivision: "CA",
  zip: "95014",
}); // true
isAddressValid({
  country: "US",
  addressLine1: "1 Infinite Loop",
  city: "Cupertino",
  subdivision: "CA",
  zip: "98014", // <- InvalidZip
}); // false

Formatter

formatAddress

Formats an address object into a string.

import { formatAddress } from "lib-address";
formatAddress({
  country: "AG",
  addressLine1: "123 Main St",
});

// 123 Main St
formatAddress({
  country: "FR",
  addressLine1: "1 rue de Rivoli",
  city: "Paris",
  zip: "75001",
});

/**
 * 1 rue de Rivoli
 * 75001 Paris
 */
formatAddress({
  country: "US",
  state: "CA",
  addressLine1: "1600 Amphitheatre Parkway",
  city: "Mountain View",
  zip: "94043",
});

/**
 * 1600 Amphitheatre Parkway
 * MOUNTAIN VIEW, CALIFORNIA 94043
 */

formatAddress can also be used to add recipient information to the address following country specifc formats.

formatAddress({
  country: "US",
  state: "CA",
  addressLine1: "1600 Amphitheatre Parkway",
  city: "Mountain View",
  zip: "94043",
  name: "John Doe",
  organization: "Acme Corp",
});

/**
 * John Doe
 * Acme Corp
 * 1600 Amphitheatre Parkway
 * MOUNTAIN VIEW, CALIFORNIA 94043
 */
formatAddress({
  country: "FR",
  organization: "Acme Corp",
  name: "John Doe",
  addressLine1: "1 rue de Rivoli",
  city: "Paris",
  zip: "75001",
});

/**
 * Acme Corp
 * John Doe
 * 1 rue de Rivoli
 * 75001 PARIS
 */
options

formatAddress accepts an optional options object as a second argument.

type FormatAddressOptions = {
  /**
   * @description Preserve input case if true
   * @default false
   */
  preserveCase?: boolean;
  /**
   * @description Use ISO 3166-2 subdivision code for state if true. Otherwise, use the state name
   * For example, use "CA" instead of "California"
   *
   * Some countries do not have ISO 3166-2 subdivision codes and will always use the state name regardless of this option
   * For example, "HK" (Hong Kong) and "KY" (Cayman Islands)
   *
   * @default false
   */
  useStateISOCode?: boolean;
  /**
   * @description Append country name to the end of the address if true. Useful for international addresses
   * @default false
   */
  appendCountry?: boolean;
  /**
   * @description Use ISO 3166-1 alpha-2 country code for country if true. Otherwise, use the country name
   * @default false
   */
  useCountryISOCode?: boolean;
  /**
   * @description Lang to use for country and subdivision names. If not provided, the default lang is used. Not all countries support multiple langs
   */
  lang?: string;
  /**
   * @description Use latinised equivalents for country, subdivision names and format if true. Not all countries support latinised equivalents
   * @default false
   */
  useLatin?: boolean;
};

Helpers

Helpers are functions that return data about a country. They are useful to build forms based on country rules.

getCountryFields

Returns an object containing all the fields for a country and their rules.

import { getCountryFields } from "lib-address";

const countryFields = getCountryFields("US");
{
  "addressLine1": "required",
  "addressLine2": "optional",
  "addressLine3": "optional",
  "state": "required",
  "zip": "required",
  "city": "required"
}

getRequiredFields

Returns an array of required fields for a country.

import { getRequiredFields } from "lib-address";

const countryFields = getRequiredFields("US");
["state", "zip", "city", "addressLine1"]

getOptionnalFields

Returns an array of optionnal fields for a country.

import { getOptionnalFields } from "lib-address";

const countryFields = getOptionnalFields("US");
["state", "zip", "city", "addressLine1"]

getZipExamples

Returns an array of zip code examples for a country.

[!NOTE] If the country does not have zip code examples, an empty array is returned.

import { getZipExamples } from "lib-address";

const countryFields = getZipExamples("US");
["95014", "22162-1010"]

Can also be used for subdivisions.

import { getZipExamples } from "lib-address";

const countryFields = getZipExamples("US", "CA");
["90000", "96199"]

getCountryStates

Returns an array of states for a country.

[!NOTE] If the country does not have states, an empty array is returned.

import { getCountryStates } from "lib-address";

const countryFields = getCountryStates("US");
[
  {
    "code": "AL",
    "name": "Alabama"
  },
  {
    "code": "AK",
    "name": "Alaska"
  },
  ...
]

Zod

This library exports a Zod schema for validating addresses.

[!NOTE] The Zod schema is not exported by default in order to reduce the size of the package. If you want to use it, you need to import it from lib-address/zod. Zod is not a dependency of this library and needs to be installed separately.

import { getAddressSchema } "lib-address/zod";
import { z } from "zod";

const formSchema = z.object({
  name: z.string().min(1),
  address: getAddressSchema(),
});

function validateForm(data: unknown) {
  return formSchema.parse(data);
}

[!WARNING] The Zod schema will throw if you attempt to validate a country that is not registered. (Only applicable in browser environment)

Tests

To run tests:

pnpm test

To generate a code coverage report:

pnpm coverage

The code coverage report can be viewed by opening ./coverage/index.html.

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

Please make sure to update tests as appropriate.

License

MIT