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

@basementuniverse/content-manager

v1.2.1

Published

A component for loading content assets and providing access to them

Downloads

17

Readme

Game Component: Content Manager

A component for loading content assets and providing access to them.

Installation

npm install @basementuniverse/content-manager

How to use

Initialise the content manager before use:

import ContentManager from '@basementuniverse/content-manager';

ContentManager.initialise();

Load content assets:

ContentManager.load([
  {
    name: 'my-item-1',
    type: 'json',
    args: ['https://some-url.com/test.json'],
  },
]);

Note: when using the "json" or "text" loaders, the argument can either be a URL to the content or the content itself. For example:

ContentManager.load([
  {
    name: 'my-item-1',
    type: 'json',
    args: [{ a: 1 }],
  },
]);

Check content manager progress and status:

console.log(ContentManager.progress); // a number between 0 (nothing loaded yet) and 1 (finished loading)
console.log(ContentManager.status); // 0: Idle, 1: Loading, 2: Processing, 3: Ready

Fetch a content asset:

const item = ContentManager.get('my-item-1');

Options

const options = { ... };
ContentManager.initialise(options);

| Option | Type | Default | Description | | --- | --- | --- | --- | | loaders | ContentLoaderMap | (see below) | A dictionary of content loader functions | | processors | ContentProcessorMap | (see below) | A dictionary of content processor functions | | simulateSlowLoading | boolean | false | If true, simulate long loading times | | slowLoadingTimeMin | number | 1000 | Minimum simulated loading time in ms | | slowLoadingTimeMax | number | 3000 | Maximum simulated loading time in ms | | simulateSlowProcessing | boolean | false | If true, simulate long processing times | | slowProcessingTimeMin | number | 1000 | Minimum simulated processing time in ms | | slowProcessingTimeMax | number | 3000 | Maximum simulated processing time in ms | | throwOnNotFound | boolean | false | Throw an error when an unknown content item is fetched with get() |

Loaders

A loader is a function which takes some arguments (as defined in the content list which is passed to ContentManager.load, as mentioned above) and returns some kind of content asset.

type ContentLoader = <T>(...args: any[]) => Promise<T>;

Some basic loaders are provided by default:

{
  "json": JSONLoader,    // Loads JSON (either inline or from a URL)
  "font": FontLoader,    // Loads a font from a URL and returns a FontFace
  "image": ImageLoader,  // Loads an image from a URL and returns an HTMLImageElement
  "audio": AudioLoader,  // Loads an audio file from a URL and returns an HTMLAudioElement
  "text": TextLoader,    // Loads text (either inline or from a URL)
}

Implementing a custom content loader

Define a function with a signature that matches ContentLoader:

import { ContentLoader } from '@basementuniverse/content-manager';

export const MyCustomLoader: ContentLoader = async <HTMLImageElement>(
  url: string
): Promise<HTMLImageElement> => {
  return new Promise<HTMLImageElement>((resolve, reject) => {
    const image = new Image();
    image.src = url;
    image.addEventListener('load', () => {
      resolve(image as any);
    });
    image.addEventListener('error', () => {
      reject(`Error loading image "${url}"`);
    });
  });
};

Register the loader when initialising the content manager:

ContentManager.initialise({
  loaders: {
    custom: MyCustomLoader,
  },
});

Note: the key (in this example: custom) is the name of the loader.

Load content assets using the custom loader:

ContentManager.load([
  {
    name: 'my-custom-item-1',
    type: 'custom',
    args: ['./test.png'],
  },
]);

Processors

A processor is a function which takes the full list of loaded content, the current content item being processed, and some optional arguments. This function might mutate the content item or add new content items to the list.

Processor functions are run after all content items have been loaded.

This could be useful for post-processing, e.g. chopping up a texture atlas into multiple textures, unpacking compressed data, or linking together multiple content items into a data structure, etc.

export type ContentProcessor = <T = any>(
  content: Record<string, ContentItem>,
  item: ContentItem<T>,
  ...args: any[]
) => Promise<void>;

Implementing a custom content processor

Define a function with a signature that matches ContentProcessor:

import { ContentProcessor } from '@basementuniverse/content-manager';

export const MyCustomProcessor: ContentProcessor = async (
  content,
  item
): Promise<void> => {
  // Do some processing here...

  // We can modify `content` (e.g. by adding/removing/changing items)
  // We can also modify `item` (e.g. by swapping out `item.content`)
};

Register the processor when initialising the content manager:

ContentManager.initialise({
  processors: {
    custom: MyCustomProcessor,
  },
});

Note: the key (in this example: custom) is the name of the processor.

Load content assets and process them using the custom processor:

ContentManager.load([
  {
    name: 'my-custom-item-1',
    type: 'image',
    args: ['./test.png'],
    processors: [
      {
        name: 'custom',
        args: [], // Optional, will be passed to the processor as the 3rd+ args
      },
    ],
  },
]);

Image Name Processor

A processor is provided which converts fields in JSON content objects which contain image names into actual image objects. The image names refer to content items which have been loaded with the image loader.

By default the processor recursively searches for fields called imageName and replaces them with fields called image, however this is configurable.

Example:

content.json:

[
  {
    "name": "sky",
    "type": "image",
    "args": ["./sky.png"]
  },
  {
    "name": "tree",
    "type": "image",
    "args": ["./tree.png"]
  },
  {
    "name": "level-data",
    "type": "json",
    "args": [
      {
        "background": {
          "imageName": "sky",
          "position": [0, 0]
        },
        "objects": [
          {
            "imageName": "tree",
            "position": [100, 100]
          },
          {
            "imageName": "tree",
            "position": [200, 100]
          }
        ]
      }
    ],
    "processors": [
      {
        "name": "imageName",
        "args": [
          {
            "imageNameFieldName": "imageName",
            "imageFieldName": "image"
          }
        ]
      }
    ]
  }
]

Note: the args provided to the processor are optional. In the example above we have explicitly defined the field names to show how it's done, but if omitted the processor will default to these values anyway.

Then we initialise the content manager and start loading content:

import * as content from './content.json';

ContentManager.initialise();
ContentManager.load(content);

This should result in the following content items being available:

const skyImage = ContentManager.get('sky'); // HTMLImageElement

const treeImage = ContentManager.get('tree'); // HTMLImageElement

const levelData = ContentManager.get('level-data');
// {
//   background: {
//     image: HTMLImageElement, <-- the 'sky' content item
//     position: [0, 0],
//   },
//   objects: [
//     {
//       image: HTMLImageElement, <-- the 'tree' content item
//       position: [100, 100],
//     },
//     {
//       image: HTMLImageElement, <-- the 'tree' content item
//       position: [200, 100],
//     },
//   ],
// }

Compiling content

A utility script is provided for compiling and minifying content.

ts-node compile-content.ts -i 'content.json' -o 'content-compiled.json'

This script will:

  1. open the input file (in the example above this is content.json)
  2. fetch any text/json items which have a file path or URL as their first argument
  3. insert their contents directly into the content JSON
  4. save the resulting JSON into the output file