Skip to content

Commit

Permalink
feat(initial functional and minimally tested version.): initial funct…
Browse files Browse the repository at this point in the history
…ionality
  • Loading branch information
softberry committed Mar 24, 2024
1 parent a85d1c6 commit 58997fd
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 63 deletions.
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Operations System Files
.DS_Store
**/.DS_Store
._.DS_Store
**/._.DS_Store

# Logs
logs
*.log
Expand Down Expand Up @@ -117,3 +123,6 @@ dist

# Compiled code
lib/

# Generated files
blurhash_encoder
74 changes: 45 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
[![Commitizen Friendly][commitizen-img]][commitizen-url]
[![Semantic Release][semantic-release-img]][semantic-release-url]

> My awesome module
> blurhash-map
## Install

Expand All @@ -19,44 +19,60 @@ npm install blurhash-map
## Usage

```ts
import { myPackage } from 'blurhash-map';

myPackage('hello');
//=> 'hello from my package'
import { BlurHashMap, BlurHashMapConfig } from 'blurhash-map';
const config: BlurHashMapConfig = {
assetsRoot: 'assets/images/samples',
hashMapJsonPath: 'test/fixtures/lib/hashmap.json',
imageExtensions: 'jpg,png,jpeg',
components: { x: 4, y: 3 },
};
const blurHashMap = new BlurHashMap(config);
blurHashMap.init();
// Now, there should be files created next to images
// with the same name but having `.hash` suffix. You should commit these files
// to avoid re-generate them next time you run `BlurHashMap`.
```

## API

### myPackage(input, options?)
### BlurHashMap(config)

#### config

Type: `BlurHashMapConfig`

assetsRoot: string; // Required. Where to find the images
hashMapJsonPath: string; // Required. Where to save generated JSON
imageExtensions: Array<string>; // Optional. Define which image files. Default : 'jpg' | 'jpeg' | 'png' | 'bmp' | 'webp'
components?: { x: number; y: number }; // Optional between 1-9. Default {x:4,y:3}. Higher is more detailed blur but longer string

#### input
#### init()

Type: `string`
Initialize `BlurHashMap` asynchronously. Creates `.hash` files and generates `hash-map.json`.

Lorem ipsum.
### generateOrDelete(imageOrHashFilePath: string, skipIfHasHash = false)

#### options
Generates a hash file if image is found. If Image is not found, deletes the `.hash` file of it.

Type: `object`
### getShortPath(file: string): string

##### postfix
Returns the relative path of the given `file` to the `assetsRoot`

Type: `string`
Default: `rainbows`
### async createJson(): Promise<void>

Lorem ipsum.
Creates `hash-map.json` from the found `.hash` files

[build-img]:https://github.com/softberry/blurhash-map/actions/workflows/release.yml/badge.svg
[build-url]:https://github.com/softberry/blurhash-map/actions/workflows/release.yml
[downloads-img]:https://img.shields.io/npm/dt/blurhash-map
[downloads-url]:https://www.npmtrends.com/blurhash-map
[npm-img]:https://img.shields.io/npm/v/blurhash-map
[npm-url]:https://www.npmjs.com/package/blurhash-map
[issues-img]:https://img.shields.io/github/issues/softberry/blurhash-map
[issues-url]:https://github.com/softberry/blurhash-map/issues
[codecov-img]:https://codecov.io/gh/softberry/blurhash-map/branch/main/graph/badge.svg
[codecov-url]:https://codecov.io/gh/softberry/blurhash-map
[semantic-release-img]:https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg
[semantic-release-url]:https://github.com/semantic-release/semantic-release
[commitizen-img]:https://img.shields.io/badge/commitizen-friendly-brightgreen.svg
[commitizen-url]:http://commitizen.github.io/cz-cli/
[build-img]: https://github.com/softberry/blurhash-map/actions/workflows/release.yml/badge.svg
[build-url]: https://github.com/softberry/blurhash-map/actions/workflows/release.yml
[downloads-img]: https://img.shields.io/npm/dt/blurhash-map
[downloads-url]: https://www.npmtrends.com/blurhash-map
[npm-img]: https://img.shields.io/npm/v/blurhash-map
[npm-url]: https://www.npmjs.com/package/blurhash-map
[issues-img]: https://img.shields.io/github/issues/softberry/blurhash-map
[issues-url]: https://github.com/softberry/blurhash-map/issues
[codecov-img]: https://codecov.io/gh/softberry/blurhash-map/branch/main/graph/badge.svg
[codecov-url]: https://codecov.io/gh/softberry/blurhash-map
[semantic-release-img]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg
[semantic-release-url]: https://github.com/semantic-release/semantic-release
[commitizen-img]: https://img.shields.io/badge/commitizen-friendly-brightgreen.svg
[commitizen-url]: http://commitizen.github.io/cz-cli/
80 changes: 64 additions & 16 deletions src/blur-hash-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,30 @@ import {
} from 'fs';
import { extname, resolve } from 'path';
type ComponentRange = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;

export type AllowedImageTypes = 'jpg' | 'jpeg' | 'png' | 'bmp' | 'webp';
export type AllowedImageTypeList = AllowedImageTypes[];
const ALLOWED_IMAGE_TYPES: AllowedImageTypeList = [
'jpg',
'jpeg',
'png',
'bmp',
'webp',
];
const DEFAULT_COMPONENT_RATIO: DefaultComponentRatio = { x: 4, y: 3 };
const C_ROOT = 'src/C';
const EXEC_INITIAL = 'src/C/blurhash_encoder';
const EXEC_MAIN = 'blurhash_encoder';

export interface DefaultComponentRatio {
x: ComponentRange;
y: ComponentRange;
}

export interface BlurHashMapConfig {
assetsRoot: string;
execMain: string;
execInitial: string;
cRoot: string;
makeCmd: string;
hashMapJsonPath: string;
imageExtensions: string;
imageExtensions?: AllowedImageTypeList;
components?: { x: ComponentRange; y: ComponentRange };
}

Expand All @@ -25,31 +41,41 @@ export type BlurHashMapData = [string, string][];
export class BlurHashMap {
config: BlurHashMapConfig & {
components: { x: ComponentRange; y: ComponentRange };
makeCmd: string;
cRoot: string;
execInitial: string;
execMain: string;
imageExtensions: AllowedImageTypeList;
};

constructor(config: BlurHashMapConfig) {
this.config = {
...config,
assetsRoot: resolve(config.assetsRoot),
execMain: resolve(config.execMain),
execInitial: resolve(config.execInitial),
cRoot: resolve(config.cRoot),
execMain: resolve(EXEC_MAIN),
execInitial: resolve(EXEC_INITIAL),
cRoot: resolve(C_ROOT),
hashMapJsonPath: resolve(config.hashMapJsonPath),
components: config.components || { x: 4, y: 3 },
components: config.components || DEFAULT_COMPONENT_RATIO,
makeCmd: 'make blurhash_encoder',
imageExtensions: config.imageExtensions || ALLOWED_IMAGE_TYPES,
};
}

async init(): Promise<void> {
const imageFiles = this.getAllImageFiles();
console.log('image files', imageFiles);
this.createExecutableIfNotFound();

if (!this.checkAllowedFileExtensions()) {
console.log('***');
return;
}
imageFiles.forEach(imageFilePath => {
this.generateOrDelete(imageFilePath, true);
});
return this.createJson();
}

generateOrDelete(imageOrHashFilePath: string, skipIfHasHash = false): void {
console.log(imageOrHashFilePath);
if (extname(imageOrHashFilePath) === '.hash') {
const hashFilePath = imageOrHashFilePath;
const imagePath = this.hashToImagePath(imageOrHashFilePath);
Expand Down Expand Up @@ -107,8 +133,28 @@ export class BlurHashMap {
});
}

private checkAllowedFileExtensions() {
const allowedExt = ALLOWED_IMAGE_TYPES;
const confFiles = this.config.imageExtensions;
const notAllowedExtList = confFiles.filter(
ext => !allowedExt.includes(ext)
);

if (notAllowedExtList.length > 0) {
throw new Error(
'❌ only ' +
allowedExt.toString() +
' are allowed. Remove these extensions: ' +
notAllowedExtList.toString()
);
}
return true;
}

private getAllImageFiles(): string[] {
const strGlobRoot = `${this.config.assetsRoot}/**/*.{${this.config.imageExtensions}}`;
const strGlobRoot = `${
this.config.assetsRoot
}/**/*.{${this.config.imageExtensions.join(',')}}`;
return globSync(strGlobRoot);
}

Expand All @@ -117,9 +163,11 @@ export class BlurHashMap {
}

private isImageFile(imageFilePath: string): boolean {
const ext = extname(imageFilePath).toLowerCase().slice(1);
const ext = extname(imageFilePath)
.toLowerCase()
.slice(1) as AllowedImageTypes;
const isInAssets = this.isFileInPath(imageFilePath);
const isImage = this.config.imageExtensions.split(',').includes(ext);
const isImage = this.config.imageExtensions.includes(ext);
return isInAssets && isImage;
}

Expand Down Expand Up @@ -218,7 +266,7 @@ export class BlurHashMap {

if (this.isImageFile(imageFilePath)) {
const execCommand = `${this.config.execMain} ${this.config.components.x} ${this.config.components.y} ${imageFilePath}`;

Check warning

Code scanning / CodeQL

Unsafe shell command constructed from library input Medium

This string concatenation which depends on
library input
is later used in a
shell command
.
This string concatenation which depends on
library input
is later used in a
shell command
.
This string concatenation which depends on
library input
is later used in a
shell command
.
// this.config.execMain + ' 8 6 ' + imageFilePath

const hash = execSync(execCommand).toString();

Check warning

Code scanning / CodeQL

Shell command built from environment values Medium

This shell command depends on an uncontrolled
absolute path
.
writeFileSync(imageHashFile, hash);
console.log(`✅ ${this.getShortPath(imageHashFile)} has been created\n`);
Expand Down
7 changes: 6 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
export { BlurHashMap } from './blur-hash-map';
export type { BlurHashMapConfig, BlurHashMapData } from './blur-hash-map';
export type {
BlurHashMapConfig,
BlurHashMapData,
AllowedImageTypeList,
DefaultComponentRatio,
} from './blur-hash-map';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
this is a file to skip no-image files
1 change: 0 additions & 1 deletion test/fixtures/assets/images/samples/test-image-1.jpeg.hash

This file was deleted.

1 change: 0 additions & 1 deletion test/fixtures/assets/images/samples/test-image-2.jpeg.hash

This file was deleted.

1 change: 0 additions & 1 deletion test/fixtures/assets/images/samples/test-image-3.jpeg.hash

This file was deleted.

51 changes: 37 additions & 14 deletions test/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
import { BlurHashMap, BlurHashMapConfig } from '../src';
import { existsSync, unlinkSync } from 'fs';
import { existsSync, readFileSync, unlinkSync } from 'fs';
import { globSync } from 'glob';
import { resolve } from 'path';
import { AllowedImageTypes } from '../src/blur-hash-map';
const config: BlurHashMapConfig = {
assetsRoot: 'test/fixtures/assets/images/samples',
cRoot: 'src/C',
execInitial: 'src/C/blurhash_encoder',
execMain: 'test/fixtures/blurhash_encoder',
hashMapJsonPath: 'test/fixtures/lib/hashmap.json',
imageExtensions: 'jpg,png,jpeg',
makeCmd: 'make blurhash_encoder',
imageExtensions: ['jpg', 'jpeg', 'png'],
};

const hashmapJSON = [
['/test-image-3.jpeg', 'LmQvL5o#?wjFtRt8ofWA%gV@M_j['],
['/test-image-2.jpeg', 'L9SgUN9S~H~Go;Wlj2xc^a$_ES5F'],
['/test-image-1.jpeg', 'LIJQ}%-;4mof%Q%MIVoLpLxvs;oL'],
];

const cleanUpRestOver = () => {
const pattern = `${resolve(config.assetsRoot)}/**/*.hash`;
const hashFiles = globSync(pattern);
const blurHashMap = new BlurHashMap(config);
for (const hash of hashFiles) {
unlinkSync(hash);
}
try {
unlinkSync(resolve(config.execInitial));
unlinkSync(resolve(config.execMain));
unlinkSync(resolve(config.hashMapJsonPath));
unlinkSync(blurHashMap.config.execInitial);
unlinkSync(blurHashMap.config.execMain);
unlinkSync(blurHashMap.config.hashMapJsonPath);
} catch (e) {
//
}
Expand All @@ -30,18 +34,37 @@ describe('index', () => {
beforeAll(() => {
cleanUpRestOver();
});
afterAll(() => {
cleanUpRestOver();
});

describe('blurhash-map', () => {
const blurHashMap = new BlurHashMap(config);
it('should create shortPath correctly', () => {
const shortPath = '/test.jpg';
const createdShortPath = blurHashMap.getShortPath(
blurHashMap.config.assetsRoot + '/test.jpg'
);
expect(createdShortPath).toEqual(shortPath);
});

it('should return a string containing the message', () => {
const f = jest.fn();
void blurHashMap.init().then(() => {
f();
});
it('should create hashmap and json', async () => {
await blurHashMap.init();

expect(existsSync(blurHashMap.config.execMain)).toBe(true);
expect(existsSync(blurHashMap.config.execInitial)).toBe(false);
expect(
JSON.parse(readFileSync(blurHashMap.config.hashMapJsonPath).toString())
).toEqual(hashmapJSON);
});

it('should throw if not allowed file extension configured', async () => {
const txt = 'txt' as AllowedImageTypes;
const blurHashMapError = new BlurHashMap({
...config,
imageExtensions: ['jpeg', txt],
});
await expect(blurHashMapError.init()).rejects.toThrow('');
});
});
});

0 comments on commit 58997fd

Please sign in to comment.