Skip to content

Commit

Permalink
- add spine cache buster
Browse files Browse the repository at this point in the history
- add sprite sheet cache buster
- tests
  • Loading branch information
GoodBoyDigital committed Apr 25, 2024
1 parent 7e8c7bd commit fbbce23
Show file tree
Hide file tree
Showing 10 changed files with 512 additions and 4 deletions.
2 changes: 1 addition & 1 deletion packages/cache-buster/src/cacheBuster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@ export function cacheBuster(): AssetPipe
newAsset.buffer = asset.buffer;

return [newAsset];
}
},
};
}
2 changes: 1 addition & 1 deletion packages/spine/src/AtlasView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export class AtlasView

getTextures(): string[]
{
const regex = /^.+?(?:\.png|\.jpg|\.jpeg)$/gm;
const regex = /^.+?(?:\.png|\.jpg|\.jpeg|\.webp|\.avif)$/gm;

const matches = this.rawAtlas.match(regex);

Expand Down
1 change: 1 addition & 0 deletions packages/spine/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './spineAtlasMipmap';
export * from './spineAtlasCompress';
export * from './spineAtlasCacheBuster';
92 changes: 92 additions & 0 deletions packages/spine/src/spineAtlasCacheBuster.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import type { Asset, PluginOptions } from '@play-co/assetpack-core';
import { checkExt, type AssetPipe, findAssetsWithFileName } from '@play-co/assetpack-core';
import type { MipmapOptions } from '@play-co/assetpack-plugin-mipmap-compress';
import { AtlasView } from './AtlasView';
import { removeSync, writeFileSync } from 'fs-extra';

export type SpineCacheBusterOptions = PluginOptions<'fix' | 'nc'> & MipmapOptions;

/**
* This should be used after the cache buster plugin in the pipes.
* As it relies on the cache buster plugin to have already cache busted all files.
* This corrects the atlas files to point to the new cache busted textures.
* At the same time it updates the hash of the files.
*
* As this pipe needs to know about all the textures in the atlas files most of the work is done
* in the finish method.
*
* Kind of like applying a patch at the end of the transform process.
*
* @param _options
* @returns
*/
export function spineAtlasCacheBuster(_options?: SpineCacheBusterOptions): AssetPipe<SpineCacheBusterOptions>
{
const defaultOptions = {
..._options,
tags: {
fix: 'fix',
..._options?.tags
},
};

const atlasFileToFix: Asset[] = [];

return {
folder: false,
name: 'spine-cache-buster',
defaultOptions,
test(asset: Asset, _options)
{
return checkExt(asset.path, '.atlas');
},

async transform(asset: Asset, _options)
{
atlasFileToFix.push(asset);

return [asset];
},

async finish(asset: Asset)
{
// first we retrieve the final transformed children - so the atlas files that have been copied
// to the output folder.
const atlasAssets = atlasFileToFix.map((asset) => asset.getFinalTransformedChildren()[0]);

atlasAssets.forEach((atlasAsset) =>
{
// we are going to replace the textures in the atlas file with the new cache busted textures
// as we do this, the hash of the atlas file will change, so we need to update the path
// and also remove the original file.

const originalHash = atlasAsset.hash;
const originalPath = atlasAsset.path;

const atlasView = new AtlasView(atlasAsset.buffer);

atlasView.getTextures().forEach((texture) =>
{
const textureAssets = findAssetsWithFileName((asset) =>
asset.filename === texture, asset, true);

// last transformed child is the renamed texture
const cacheBustedTexture = textureAssets[0].getFinalTransformedChildren()[0];

atlasView.replaceTexture(texture, cacheBustedTexture.filename);
});

atlasAsset.buffer = atlasView.buffer;

atlasAsset.path = atlasAsset.path.replace(originalHash, atlasAsset.hash);

removeSync(originalPath);

// rewrite..
writeFileSync(atlasAsset.path, atlasAsset.buffer);
});

atlasFileToFix.length = 0;
}
};
}
96 changes: 94 additions & 2 deletions packages/spine/test/spineAtlasAll.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { AssetPack } from '@play-co/assetpack-core';
import { mipmapCompress } from '@play-co/assetpack-plugin-mipmap-compress';
import { readFileSync } from 'fs-extra';
import { existsSync, readFileSync } from 'fs-extra';
import { assetPath, createFolder, getInputDir, getOutputDir } from '../../../shared/test';
import { spineAtlasCompress } from '../src/spineAtlasCompress';
import { spineAtlasMipmap } from '../src/spineAtlasMipmap';
import { cacheBuster } from '@play-co/assetpack-plugin-cache-buster';
import { spineAtlasCacheBuster } from '../src/spineAtlasCacheBuster';

const pkg = 'spine';

describe('Spine Atlas All', () =>
{
it.only('should correctly create files when Mipmap and Compress are used', async () =>
it('should correctly create files when Mipmap and Compress are used', async () =>
{
const testName = 'spine-atlas-compress-mip';
const inputDir = getInputDir(pkg, testName);
Expand Down Expand Up @@ -73,4 +75,94 @@ describe('Spine Atlas All', () =>
expect(rawAtlasWebpHalf.includes('[email protected]')).toBeTruthy();
expect(rawAtlasWebpHalf.includes('[email protected]')).toBeTruthy();
});

it.only('should correctly create files when Mipmap and CacheBuster are used', async () =>
{
const testName = 'spine-atlas-mip-cache-buster';
const inputDir = getInputDir(pkg, testName);
const outputDir = getOutputDir(pkg, testName);

createFolder(
pkg,
{
name: testName,
files: [
{
name: 'dragon{spine}.atlas',
content: assetPath(pkg, 'dragon.atlas'),
},
{
name: 'dragon.png',
content: assetPath(pkg, 'dragon.png'),
},
{
name: 'dragon2.png',
content: assetPath(pkg, 'dragon2.png'),
},
],
folders: [],
});

const assetpack = new AssetPack({
entry: inputDir,
output: outputDir,
cache: false,
pipes: [
mipmapCompress({
mipmap: {
resolutions: { default: 1, low: 0.5 },
},
compress: {
png: true,
jpg: true,
webp: true,
}
}),
spineAtlasMipmap({
resolutions: { default: 1, low: 0.5 },
}),
spineAtlasCompress({
png: true,
jpg: true,
webp: true,
}),
cacheBuster(),
spineAtlasCacheBuster()
]
});

await assetpack.run();

[
{
atlas: `[email protected]`,
png1: `[email protected]`,
png2: `[email protected]_22pw.webp`
},
{
atlas: `dragon.webp-spj8.atlas`,
png1: `dragon-rSwKOg.webp`,
png2: `dragon2-ws3uhw.webp`
},
{
atlas: `[email protected]`,
png1: `[email protected]`,
png2: `[email protected]`
},
{
atlas: `dragon.png-O471eg.atlas`,
png1: `dragon-vezElA.png`,
png2: `dragon2-3UnJNw.png`
}
].forEach(({ atlas, png1, png2 }) =>
{
const rawAtlas = readFileSync(`${outputDir}/${atlas}`);

expect(rawAtlas.includes(png1)).toBeTruthy();
expect(rawAtlas.includes(png2)).toBeTruthy();

expect(existsSync(`${outputDir}/${png1}`)).toBeTruthy();
expect(existsSync(`${outputDir}/${png2}`)).toBeTruthy();
});
});
});
55 changes: 55 additions & 0 deletions packages/spine/test/spineAtlasCacheBuster.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { AssetPack } from '@play-co/assetpack-core';
import { cacheBuster } from '@play-co/assetpack-plugin-cache-buster';
import { readFileSync } from 'fs-extra';
import { assetPath, createFolder, getInputDir, getOutputDir } from '../../../shared/test';
import { spineAtlasCacheBuster } from '../src/spineAtlasCacheBuster';

const pkg = 'spine';

describe('Spine Atlas Cache Buster', () =>
{
it('should modify the atlas to include the correct file names when cache busting applied', async () =>
{
const testName = 'spine-atlas-cache-bust';
const inputDir = getInputDir(pkg, testName);
const outputDir = getOutputDir(pkg, testName);

createFolder(
pkg,
{
name: testName,
files: [
{
name: 'dragon{spine}.atlas',
content: assetPath(pkg, 'dragon.atlas'),
},
{
name: 'dragon.png',
content: assetPath(pkg, 'dragon.png'),
},
{
name: 'dragon2.png',
content: assetPath(pkg, 'dragon2.png'),
},
],
folders: [],
});

const assetpack = new AssetPack({
entry: inputDir,
output: outputDir,
cache: false,
pipes: [
cacheBuster(),
spineAtlasCacheBuster(),
]
});

await assetpack.run();

const rawAtlas = readFileSync(`${outputDir}/dragon-qmTByg.atlas`);

expect(rawAtlas.includes('dragon-iSqGPQ')).toBeTruthy();
expect(rawAtlas.includes('dragon2-6ebkeA')).toBeTruthy();
});
});
1 change: 1 addition & 0 deletions packages/texture-packer/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './texturePacker';
export * from './texturePackerCompress';
export * from './texturePackerCacheBuster';
93 changes: 93 additions & 0 deletions packages/texture-packer/src/texturePackerCacheBuster.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import type { Asset, PluginOptions } from '@play-co/assetpack-core';
import { checkExt, type AssetPipe, findAssetsWithFileName } from '@play-co/assetpack-core';
import type { MipmapOptions } from '@play-co/assetpack-plugin-mipmap-compress';
import { removeSync, writeFileSync } from 'fs-extra';

export type SpineOptions = PluginOptions<'fix' | 'nc'> & MipmapOptions;

/**
* This should be used after the cache buster plugin in the pipes.
* As it relies on the cache buster plugin to have already cache busted all files.
* This corrects the sprite sheet files to point to the new cache busted textures.
* At the same time it updates the hash of the files.
*
* As this pipe needs to know about all the textures in the texture files most of the work is done
* in the finish method.
*
* Kind of like applying a patch at the end of the transform process.
*
* @param _options
* @returns
*/
export function texturePackerCacheBuster(_options?: SpineOptions): AssetPipe<SpineOptions>
{
const defaultOptions = {
template: '@%%x',
resolutions: { default: 1, low: 0.5 },
fixedResolution: 'default',
..._options,
tags: {
fix: 'fix',
..._options?.tags
},
};

const textureJsonFilesToFix: Asset[] = [];

return {
folder: false,
name: 'texture-packer-cache-buster',
defaultOptions,
test(asset: Asset, _options)
{
return asset.allMetaData.tps && checkExt(asset.path, '.json');
},

async transform(asset: Asset, _options)
{
textureJsonFilesToFix.push(asset);

return [asset];
},

async finish(asset: Asset)
{
// first we retrieve the final transformed children - so the atlas files that have been copied
// to the output folder.
const jsonAssets = textureJsonFilesToFix.map((asset) => asset.getFinalTransformedChildren()[0]);

jsonAssets.forEach((jsonAsset) =>
{
// we are going to replace the textures in the atlas file with the new cache busted textures
// as we do this, the hash of the atlas file will change, so we need to update the path
// and also remove the original file.

const originalHash = jsonAsset.hash;
const originalPath = jsonAsset.path;

const json = JSON.parse(jsonAsset.buffer.toString());

const texture = json.meta.image;

const textureAssets = findAssetsWithFileName((asset) =>
asset.filename === texture, asset, true);

// last transformed child is the renamed texture
const cacheBustedTexture = textureAssets[0].getFinalTransformedChildren()[0];

json.meta.image = cacheBustedTexture.filename;

jsonAsset.buffer = Buffer.from(JSON.stringify(json));

jsonAsset.path = jsonAsset.path.replace(originalHash, jsonAsset.hash);

removeSync(originalPath);

// rewrite..
writeFileSync(jsonAsset.path, jsonAsset.buffer);
});

textureJsonFilesToFix.length = 0;
}
};
}
Loading

0 comments on commit fbbce23

Please sign in to comment.