Typings and Versions

We are going to develop “type packages” for each API. So far we need:

They are imported and used by the real packages.

Some naming suggestions:

* @frontity/settings-types
* @frontity/types-settings
* @frontity-types/settings

Any other idea? Which one do you prefer?

Maybe we can do another one for The packages export API to type the exports:

import { Package, Namespace } from "@frontity-types/packages"

const theme: Namespace = {
  root: Theme
  fills: Fills
  Store: ThemeStore,
  components: Components,
};

export default: Package {
  theme
}

where @frontity-types/packages has things like:

interface Namespace {
  root?: React.Component;
  fills?: React.Component;
  Store: Overmind.Config;
  ...
}

interface Package {
  [key: string]: Namespace
} 

What if instead of a separate package for each type, we use only one with folders?

@frontity/types/packages
@frontity/types/settings
@frontity/types/source
@frontity/types/router
@frontity/types/comments

@development-team :point_up_2: what do you think? I need to create the packages one now :slight_smile:

I think it’s ok :slight_smile:

I think one package is ok too. :+1:

Perfect, thanks guys!

I’ve added tests for the typings of file-settings:

It’s something I saw when I did a PR to DefinitelyTyped.

They are not real jest tests, only syntax. But jest throws if typescript fails, so they are useful anyway. I think it’s a good way to test types and have a list of the things that are supported.

The main drawback is that you cannot test things that are not allowed, only things that are allowed, but it’s better than nothing.

Jest requires at least one test, so I’ve used a final test("Types are fine!", () => {}) at the end, but it’s fine because that’s what it’s shown in the jest output :slight_smile:

59

@development-team take a look at the file and let me know what you think!

We are going to use this structure for packages:

export interface Package {
  name: string;
  namespaces: string[];
  roots?: {
    [namespace: string]: React.ReactType;
  };
  fills?: {
    [namespace: string]: React.ReactType;
  };
  state?: {
    settings?: {
      [namespace: string]: {
        [key: string]: any;
      };
    };
    [namespace: string]: {
      [key: string]: any;
    };
  };
  actions?: {
    [namespace: string]: {
      [action: string]:
        | ((state: Package["state"]) => void)
        | ((state: Package["state"]) => (input: any) => void);
    };
  };
  libraries?: {
    [namespace: string]: {
      [library: string]: any;
    };
  };
  //
  // Filters are not supported yet.
  // filters: {
  //   [namespace: string]: {
  //     [filter: string]: any;
  //   };
  // };
  //
  // Sagas are not supported yet.
  // sagas: {
  //   [namespace: string]: {
  //     [saga: string]: any;
  //   };
  // };
}

I’ve been thinking about how to do versioning. It’s kind of tricky and we don’t know much yet, but I think we can start with this:

For each package, we need to know its Frontity dependencies. For example, a theme may be compatible with v1 of source and v1 of router but v2 of comments. We need a way to let it specify that.

My proposal is to use the version in the package of each type, which is the API contract anyway. That means we can’t use @frontity/types/source, we need a separate package with a separate version @frontity/source.

I think this is best explained with an example.

my-theme/package.json

  "types": "type.ts",
  "dependencies": {
    "@frontity/source": "^1.0.0",
    "@frontity/router": "^1.0.0",
    "@frontity/comments": "^2.0.0",
  }

my-theme/type.ts

import { Action, Package } from "@frontity/types"
import Router from "@frontity/router"
import Source from "@frontity/source"
import Comments from "@frontity/comments"

interface MyTheme extends Package {
  name: "my-theme";
  namespaces: Namespaces<"theme">;
  state: {
    theme: {
      prop1: number;
    };
  };
  actions: {
    theme: {
      beforeSSR: Action<MyTheme>;
    };
    router: Router["actions"]["router"];
    source: Source["actions"]["source"];
  };
  roots: {
    theme: React.ReactType;
  };
  libraries: {
    comments: Comments["libraries"]["comments"];
  };
}
export default MyTheme

If for example my-theme can work with both router v1 and v2 because it doesn’t use anything from v1 that has been deprecated, it can use an OR npm dependency:
my-theme/package.json

  "dependencies": {
    "@frontity/router": "^1.0.0 || ^2.0.0",
    ...
  }

Now, a package implementing one specific API also needs to import a type package. For example, tiny-router:
@frontity/tiny-router/package.json

  "types": "type.ts",
  "dependencies": {
    "@frontity/router": "^1.0.0",
  }

@frontity/tiny-router/type.ts

import { Action, Package } from "@frontity/types"
import Router from "@frontity/router"

interface TinyRouter extends Router {
  name: "@frontity/tiny-router";
  namespaces: Namespaces<"router">;
  state: {
    router: Router["state"]["router"] & {
      history: string[];
    };
  };
  actions: {
    router: Router["actions"]["router"];
  };
}
export default TinyRouter

Here, we need some way to know that this package implements a router. My proposal is to use a npm keyword like frontity-xxx. For tiny-router:
@frontity/tiny-router/package.json

  "keywords": [
    "frontity",
    "frontity-router",
  ],
  "dependencies": {
    "@frontity/router": "^1.0.0",
  }

That way we can know which package implements which API and which package only depends on what API.

We will be able to do a npm search for all available routers and:

  • List them in the Frontity Admin.
  • Warn the user if he is missing one package.
  • Warn the user if there’s a mismatch.
  • Upgrade the API for each type of package separately.

One of the less obvious parts is to figure out how to maintain different APIs where the second one is just an augmented version of the first one. In our case, the API of router vs the API of the 3d-router. Both need to be router, but the first one is going to be much simpler than the second one.

We can use v1 for the router (tiny-router) and v2 for the context router (3d-router).

One theme that needs contexts would need to specify it like this:
my-theme/package.json

  "dependencies": {
    "@frontity/router": "^2.0.0",
    ...
  }

but an old comments package may have declared support only for v1:
my-comments/package.json

  "dependencies": {
    "@frontity/router": "^1.0.0",
    ...
  }

One implementation of the v2 router, for example 3d-router, works with both v1 and v2 so it may use this:
@frontity/3d-router/package.json

  "keywords": [
    "frontity",
    "frontity-router"
  ],
  "dependencies": {
    "@frontity/router": "^1.0.0 || ^2.0.0",
    ...
  }

I’m not 100% sure if this is going to work fine, but we can try.

Suggestions welcomed :+1:

1 Like