1.1.2 • Published 2 years ago

roullector v1.1.2

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

roullector: route collector

npm.badge codecov.badge bundlephobia.badge actions.ci.badge actions.release.badge semantic-release.badge MIT

tweet

Collect and generate route data from a file-based router such as svelte-kit's

<!-- before -->
<script lang="ts">
  import { goto } from '$app/navigation';

  goto('/organiztion/org-abc/members/member-123/posts/prefix-too-much-literals-no-safety');
  // => 404 because organiztion is a typo
</script>

<!-- after -->
<script lang="ts">
  import { goto } from '$app/navigation';
  import { AppRoutes, route } from '$generated/routing';

  goto(
    route(
      AppRoutes.organization.$orgId.members.$memberId.posts.prefix$slug,
      'org-abc',
      'member-123',
      'type-support-no-typo',
    ),
  );
</script>

Table of Contents

Installation

npm install -D roullector
yarn add --dev roullector
pnpm install -D roullector

Usage

This package was initially built and tested for svelte-kit. But I expect it to work in other cases using the configurable options. Examples used in this docs will use svelte-kit common route setup.

Quick Start

If you are using svelte-kit and want a no lock-in jump start, do, at project root,...

npx roullector collect

...and inspect the src/generated/routing directory for the generated goodies.

Example

If you have a directory that contains some routes like below...

src/routes/
  ├── __components/
  |       ├── Navbar.svelte
  |       └── Footer.svelte
  ├── __layout.svelte
  ├── __error.svelte
  ├── me.svelte
  ├── sign-in.svelte
  ├── sign-up.svelte
  ├── index.svelte
  └── admin/
      ├── __components/
      |     ├── Admin.layout.svelte
      |     ├── types.ts
      └── users/
          ├── [id]/
          |     ├── __layout.svelte
          |     ├── index.svelte
          |     ├── types.ts
          |     └── posts/
          |           ├── [post_id].svelte
          |           ├── s-[slug].svelte
          |           └── l-[short-link]-l.svelte
          └── index.svelte

...and a npm script that run roullector with default options,...

// package.json
{
  "scripts": {
    "codegen:routing": "roullector collect"
  }
}

...the following files will be generated:

// src/generated/routing/routes.json
{
  "admin": {
    "users": {
      "$id": {
        "index": "/admin/users/[id]",
        "posts": {
          "$postId": "/admin/users/[id]/posts/[post_id]",
          "l$shortLink$L": "/admin/users/[id]/posts/l-[short-link]-l",
          "s$slug": "/admin/users/[id]/posts/s-[slug]",
          "__dir": "/admin/users/[id]/posts"
        }
      },
      "index": "/admin/users"
    },
    "__dir": "/admin"
  },
  "index": "/",
  "me": "/me",
  "signIn": "/sign-in",
  "signUp": "/sign-up"
}
// src/generated/routing/index.ts
export { default as AppRoutes } from './routes.json';
/**
 * build a complete path with injected arguments
 * @param path {string} based path
 * @param args {string[]} arguments to inject
 */
export function route(path: string, ...args: string[]): string {
  const params = path.match(/\[[a-zA-Z_-]+\]/g) ?? [];
  for (const i in params) {
    path = path.replace(params[i], args[i]);
  }
  return path;
}

Notice that index.ts will import json, you need to configure your tsconfig.json to enable resolveJsonModule:

// tsconfig.json
{
  "compilerOptions": {
    "resolveJsonModule": true
  }
}

You can then use the route helper to construct a complete path with typescript support built-in. For example:

import { AppRoutes } from '$generated/routing'; // or use relative path

const path = route(AppRoutes.admin.users.$id.posts.s$slug, 'user-id-123', 'slug');
// path = '/admin/users/user-id-123/posts/s-slug'

// ... later
// navigate(path);

Options

Run npx roullector collect help to for configurable options in command-line mode

namedefaultdescriptioncli equivalent
inDir'src/routes'input directory path to collect route data from-i, --inDir
extensions['.svelte']file extensions to accept-e, --extensions (comma-separated)
ignorePatterns[/^_/]patterns to ignore filenames-x, --ignorePatterns (comma-separated)
outputtruewrite generated files to disk?, if false will print to console--no-output
outDir'src/generated/routing'output directory-o, --outDir
depthInfinitydepth of directory traversal-d, --depth
dirkey__dirkey to save path for directories with no index file-k, --dirkey
keyTransform[dollarArgify(), camelCasify()]how to transform route key in mapping-t, --keyTransform (allows multiple)
utilstruegenerate utils for building path?--no-utils
typescripttruegenerate files in typescript?--no-typescript
verbosefalseprint more info during operation (for cli only)--verbose

Notes:

  • in command-line mode, keyTransform
    • only accepts these choices: dollarArg | camelCase | none,
    • if you want to specify multiple transforms, provide multiple arguments: --keyTransform=dollarArg --keyTransform=camelCase. The transforms will be applied in the order they are provided.
    • The rationale for the current default is to enable reference to the mapping without having to do something like AppRoutes['a-kebab-case']['[id'].
    • To opt out completely, do --keyTransform=none.
    • See implementation for more details.
  • for boolean options (default to true), the cli equivalent is --no-<option>, meaning only add the flag if you want to negate the option.

Library Usage

import {
  collect,
  defaultCollectOptions,
  camelCasify,
  dollarArgify,
  compose,
} from `roullector`;

console.log('These are default options:', defaultCollectOptions);

let { json, route } = collect(); // use default options
// json = './src/generated/routing/routes.json'
// route = './src/generated/routing/index.ts'

({ json, route } = collect({ output: false; }); // helpful for testing
// json = generated route mapping content
// route = generated index source

const transformers = [dollarArgify, camelCasify];
({ json, route } = collect({
  keyTransform: [
    (key, original) => compose(key, ...defaultTransformers),
    // key = the current key in the transformation pipeline
    // original = the original key before all transformation
  ],
}))

Contributing

Read Contribution Guide

tweet