Skip to content

Commit

Permalink
Add ability to override loadParser when loading assets (#9226)
Browse files Browse the repository at this point in the history
  • Loading branch information
GoodBoyDigital committed Mar 2, 2023
1 parent 482bdcc commit 5057217
Show file tree
Hide file tree
Showing 15 changed files with 160 additions and 16 deletions.
89 changes: 73 additions & 16 deletions packages/assets/src/loader/Loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,29 @@ import type { LoadAsset, PromiseAndParser } from './types';
export class Loader
{
private _parsers: LoaderParser[] = [];
private _parserHash: Record<string, LoaderParser>;

private _parsersValidated = false;

/** All loader parsers registered */
public parsers = new Proxy(this._parsers, {
set: (target, key, value) =>
{
this._parsersValidated = false;

target[key as any as number] = value;

return true;
}
});

/** Cache loading promises that ae currently active */
public promiseCache: Record<string, PromiseAndParser> = {};

/** function used for testing */
public reset(): void
{
this._parsersValidated = false;
this.promiseCache = {};
}

Expand All @@ -45,29 +61,51 @@ export class Loader
{
let asset = null;

for (let i = 0; i < this.parsers.length; i++)
let parser: LoaderParser = null;

// first check to see if the user has specified a parser
if (data.loadParser)
{
const parser = this.parsers[i];
// they have? lovely, lets use it
parser = this._parserHash[data.loadParser];

if (parser.load && parser.test?.(url, data, this))
if (!parser)
{
asset = await parser.load(url, data, this);
result.parser = parser;

break;
// #if _DEBUG
// eslint-disable-next-line max-len
console.warn(`[Assets] specified load parser "${data.loadParser}" not found while loading ${url}`);
// #endif
}
}

if (!result.parser)
// no parser specified, so lets try and find one using the tests
if (!parser)
{
// #if _DEBUG
// eslint-disable-next-line max-len
console.warn(`[Assets] ${url} could not be loaded as we don't know how to parse it, ensure the correct parser has being added`);
// #endif
for (let i = 0; i < this.parsers.length; i++)
{
const parserX = this.parsers[i];

return null;
if (parserX.load && parserX.test?.(url, data, this))
{
parser = parserX;
break;
}
}

if (!parser)
{
// #if _DEBUG
// eslint-disable-next-line max-len
console.warn(`[Assets] ${url} could not be loaded as we don't know how to parse it, ensure the correct parser has been added`);
// #endif

return null;
}
}

asset = await parser.load(url, data, this);
result.parser = parser;

for (let i = 0; i < this.parsers.length; i++)
{
const parser = this.parsers[i];
Expand Down Expand Up @@ -119,6 +157,11 @@ export class Loader
onProgress?: (progress: number) => void,
): Promise<T | Record<string, T>>
{
if (!this._parsersValidated)
{
this._validateParsers();
}

let count = 0;

const assets: Record<string, Promise<any>> = {};
Expand Down Expand Up @@ -206,9 +249,23 @@ export class Loader
await Promise.all(promises);
}

/** All loader parsers registered */
public get parsers(): LoaderParser[]
/** validates our parsers, right now it only checks for name conflicts but we can add more here as required! */
private _validateParsers()
{
return this._parsers;
this._parsersValidated = true;

this._parserHash = this._parsers
.filter((parser) => parser.name)
.reduce((hash, parser) =>
{
if (hash[parser.name])
{
// #if _DEBUG
console.warn(`[Assets] loadParser name conflict "${parser.name}"`);
// #endif
}

return { ...hash, [parser.name]: parser };
}, {} as Record<string, LoaderParser>);
}
}
3 changes: 3 additions & 0 deletions packages/assets/src/loader/parsers/LoaderParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ export interface LoaderParser<ASSET = any, META_DATA = any, CONFIG = Record<stri
/** A config to adjust the parser */
config?: CONFIG;

/** The name of the parser (this can be used when specifying loadParser in a LoadAsset) */
name?: string;

/**
* each URL to load will be tested here,
* if the test is passed the assets are loaded using the load function below.
Expand Down
2 changes: 2 additions & 0 deletions packages/assets/src/loader/parsers/loadJson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export const loadJson = {
priority: LoaderParserPriority.Low,
},

name: 'loadJson',

test(url: string): boolean
{
return (utils.path.extname(url).toLowerCase() === '.json');
Expand Down
3 changes: 3 additions & 0 deletions packages/assets/src/loader/parsers/loadTxt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import type { LoaderParser } from './LoaderParser';

/** Simple loader plugin for loading text data */
export const loadTxt = {

name: 'loadTxt',

extension: {
type: ExtensionType.LoadParser,
priority: LoaderParserPriority.Low,
Expand Down
2 changes: 2 additions & 0 deletions packages/assets/src/loader/parsers/loadWebFont.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ export const loadWebFont = {
priority: LoaderParserPriority.Low,
},

name: 'loadWebFont',

test(url: string): boolean
{
return checkDataUrl(url, validFontMIMEs) || checkExtension(url, validFontExtensions);
Expand Down
2 changes: 2 additions & 0 deletions packages/assets/src/loader/parsers/textures/loadSVG.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export const loadSVG = {
priority: LoaderParserPriority.High,
},

name: 'loadSVG',

test(url: string): boolean
{
return (utils.path.extname(url).toLowerCase() === '.svg');
Expand Down
3 changes: 3 additions & 0 deletions packages/assets/src/loader/parsers/textures/loadTextures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ export async function loadImageBitmap(url: string): Promise<ImageBitmap>
* @memberof PIXI
*/
export const loadTextures = {

name: 'loadTextures',

extension: {
type: ExtensionType.LoadParser,
priority: LoaderParserPriority.High,
Expand Down
10 changes: 10 additions & 0 deletions packages/assets/src/loader/types.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import type { LoaderParser } from './parsers';

/**
* The loader resource type.
* @memberof PIXI
*/
export interface LoadAsset<T=any>
{
/** The URL or relative path to the asset */
src: string;
/** Optional data */
data?: T;
/** Aliases associated with asset */
alias?: string[];
/** Format, ususally the file extension */
format?: string;
/** An override that will ensure that the asset is loaded with a specific parser */
loadParser?: 'loadTextures' | 'loadJson' | 'loadTxt' | 'loadWebFont' | 'loadSVG' | string;
}

export interface PromiseAndParser
Expand Down
Binary file added packages/assets/test/assets/textures/bunny.other
Binary file not shown.
52 changes: 52 additions & 0 deletions packages/assets/test/loader.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ describe('Loader', () =>
const loader = new Loader();

loader['_parsers'].push({
name: 'test',
test: () => true,
load: async (url, options) =>
url + options.data.whatever,
Expand Down Expand Up @@ -226,4 +227,55 @@ describe('Loader', () =>

expect(foundFont).toBe(false);
});

it('should throw a warning if a parser specified does not exist', async () =>
{
const loader = new Loader();

loader['_parsers'].push(loadTextures);

const spy = spyOn(console, 'warn');

await loader.load({
src: `${serverPath}textures/bunny.png`,
loadParser: 'chicken'
});

// eslint-disable-next-line max-len
expect(spy).toHaveBeenCalledWith(`[Assets] specified load parser "chicken" not found while loading ${serverPath}textures/bunny.png`);
});

it('should throw a warning if a parser is added with the same name', async () =>
{
const loader = new Loader();

loader['_parsers'].push(loadTextures);
loader['_parsers'].push(loadTextures);

const spy = spyOn(console, 'warn');

await loader.load({
src: `${serverPath}textures/bunny.other`,
loadParser: 'loadTextures'
});

// eslint-disable-next-line max-len
expect(spy).toHaveBeenCalledWith('[Assets] loadParser name conflict "loadTextures"');
});

it('should load and parse with specified loader', async () =>
{
const loader = new Loader();

loader['_parsers'].push(loadTextures);

const texture = await loader.load({
src: `${serverPath}textures/bunny.other`,
loadParser: 'loadTextures'
});

expect(texture.baseTexture.valid).toBe(true);
expect(texture.width).toBe(26);
expect(texture.height).toBe(37);
});
});
2 changes: 2 additions & 0 deletions packages/basis/src/loader/loadBasis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export const loadBasis = {
priority: LoaderParserPriority.High,
},

name: 'loadBasis',

test(url: string): boolean
{
return checkExtension(url, '.basis');
Expand Down
2 changes: 2 additions & 0 deletions packages/compressed-textures/src/loaders/loadDDS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export const loadDDS: LoaderParser = {
priority: LoaderParserPriority.High,
},

name: 'loadDDS',

test(url: string): boolean
{
return checkExtension(url, '.dds');
Expand Down
2 changes: 2 additions & 0 deletions packages/compressed-textures/src/loaders/loadKTX.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export const loadKTX = {
priority: LoaderParserPriority.High,
},

name: 'loadKTX',

test(url: string): boolean
{
return checkExtension(url, '.ktx');
Expand Down
2 changes: 2 additions & 0 deletions packages/spritesheet/src/spritesheetAsset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ export const spritesheetAsset = {
* @ignore
*/
loader: {
name: 'spritesheetLoader',

extension: {
type: ExtensionType.LoadParser,
priority: LoaderParserPriority.Normal,
Expand Down
2 changes: 2 additions & 0 deletions packages/text-bitmap/src/loadBitmapFont.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export const loadBitmapFont = {
priority: LoaderParserPriority.Normal,
},

name: 'loadBitmapFont',

test(url: string): boolean
{
return validExtensions.includes(utils.path.extname(url).toLowerCase());
Expand Down

0 comments on commit 5057217

Please sign in to comment.