Skip to content

Commit d8daed2

Browse files
chore: migration to typescript (#18)
1 parent 51c0971 commit d8daed2

17 files changed

+182
-92
lines changed

.eslintrc.yml

Lines changed: 0 additions & 8 deletions
This file was deleted.

.github/workflows/nodejs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ jobs:
1111
# Documentation: https://github.com/zakodium/workflows#nodejs-ci
1212
uses: zakodium/workflows/.github/workflows/nodejs.yml@nodejs-v1
1313
with:
14-
node-version-matrix: '[14, 16, 18]'
14+
lint-check-types: true

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
node_modules
2-
coverage
2+
coverage
3+
lib
4+
lib-esm

babel.config.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module.exports = {
2+
presets: ['@babel/preset-typescript'],
3+
plugins: ['@babel/plugin-transform-modules-commonjs'],
4+
};

eslint.config.mjs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import cheminfo from 'eslint-config-cheminfo-typescript';
2+
import globals from 'globals';
3+
4+
export default [
5+
...cheminfo,
6+
{
7+
languageOptions: {
8+
globals: {
9+
...globals.jest,
10+
...globals.node,
11+
},
12+
},
13+
},
14+
];

package.json

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,45 @@
22
"name": "fast-bmp",
33
"version": "2.0.1",
44
"description": "A bmp image encoder",
5-
"main": "src/index.js",
5+
"main": "./lib/index.js",
6+
"module": "./lib-esm/index.js",
7+
"types": "./lib/index.d.ts",
68
"repository": "[email protected]:image-js/fast-bmp.git",
79
"author": "Daniel Kostro <[email protected]>",
810
"license": "MIT",
11+
"files": [
12+
"src",
13+
"lib",
14+
"lib-esm"
15+
],
916
"dependencies": {
10-
"iobuffer": "^5.1.0"
17+
"iobuffer": "^5.4.0"
1118
},
1219
"devDependencies": {
13-
"@jest/globals": "^29.1.1",
14-
"eslint": "^8.24.0",
15-
"eslint-config-cheminfo": "^8.0.2",
20+
"@babel/plugin-transform-modules-commonjs": "^7.26.3",
21+
"@babel/preset-typescript": "^7.26.0",
22+
"@types/jest": "^29.5.14",
23+
"eslint": "^9.21.0",
24+
"eslint-config-cheminfo-typescript": "^17.0.0",
1625
"jest": "^29.1.1",
17-
"prettier": "^2.7.1"
26+
"prettier": "^2.7.1",
27+
"rimraf": "^6.0.1",
28+
"typescript": "^5.8.2"
1829
},
1930
"scripts": {
2031
"eslint": "eslint src",
2132
"eslint-fix": "eslint --fix src",
2233
"prettier": "prettier --check src",
2334
"prettier-write": "prettier --write src",
24-
"test": "npm run test-only && npm run eslint && npm run prettier",
35+
"test": "npm run test-only && npm run eslint && npm run prettier && npm run check-types",
2536
"test-only": "jest --coverage",
26-
"test-watch": "jest --watch",
27-
"test-write": "FAST_BMP_WRITE_DATA_FILES=1 npm run test-jest"
37+
"test-write": "FAST_BMP_WRITE_DATA_FILES=1 npm run test-only",
38+
"check-types": "tsc --noEmit",
39+
"clean": "rimraf lib lib-esm",
40+
"prepack": "npm run tsc",
41+
"tsc": "npm run clean && npm run tsc-cjs && npm run tsc-esm",
42+
"tsc-cjs": "tsc --project tsconfig.cjs.json",
43+
"tsc-esm": "tsc --project tsconfig.esm.json"
2844
},
2945
"jest": {
3046
"testEnvironment": "node"

src/BMPEncoder.js renamed to src/BMPEncoder.ts

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,47 @@
1-
'use strict';
1+
import { IOBuffer } from 'iobuffer';
22

3-
const { IOBuffer } = require('iobuffer');
3+
import { BITMAPV5HEADER } from './constants';
44

5-
const constants = require('./constants');
5+
export interface DataToEncode {
6+
/**
7+
* Image bit depth.
8+
*/
9+
bitDepth: number;
10+
/**
11+
* Image height.
12+
*/
13+
height: number;
14+
/**
15+
* Image width.
16+
*/
17+
width: number;
18+
/**
19+
* Image data.
20+
*/
21+
data: IOBuffer | ArrayBufferLike | ArrayBufferView | Buffer;
22+
/**
23+
* Image number of channels.
24+
*/
25+
channels: number;
26+
/**
27+
* Image number of channels excluding alpha.
28+
*/
29+
components: number;
30+
}
631

7-
const tableLeft = [];
32+
const tableLeft: number[] = [];
833
for (let i = 0; i <= 8; i++) {
934
tableLeft.push(0b11111111 << i);
1035
}
1136

12-
class BMPEncoder extends IOBuffer {
13-
constructor(data) {
37+
export default class BMPEncoder extends IOBuffer {
38+
width: number;
39+
height: number;
40+
bitDepth: number;
41+
channels: number;
42+
components: number;
43+
encoded: IOBuffer = new IOBuffer();
44+
constructor(data: DataToEncode) {
1445
if (data.bitDepth !== 1) {
1546
throw new Error('Only bitDepth of 1 is supported');
1647
}
@@ -34,20 +65,20 @@ class BMPEncoder extends IOBuffer {
3465
this.writeColorTable();
3566
const offset = this.encoded.offset;
3667
this.writePixelArray();
68+
const imageSize = this.encoded.getWrittenByteLength();
3769
this.encoded.rewind();
38-
this.writeBitmapFileHeader(offset);
70+
this.writeBitmapFileHeader(offset, imageSize);
3971
return this.encoded.toArray();
4072
}
4173

4274
writePixelArray() {
43-
let io = this.encoded;
75+
const io = this.encoded;
4476
const rowSize = Math.floor((this.bitDepth * this.width + 31) / 32) * 4;
4577
const dataRowSize = Math.ceil((this.bitDepth * this.width) / 8);
4678
const skipSize = rowSize - dataRowSize;
4779
const bitOverflow = (this.bitDepth * this.width) % 8;
4880
const bitSkip = bitOverflow === 0 ? 0 : 8 - bitOverflow;
4981
const totalBytes = rowSize * this.height;
50-
5182
let byteA, byteB;
5283
let offset = 0; // Current off set in the ioData
5384
let relOffset = 0;
@@ -62,6 +93,7 @@ class BMPEncoder extends IOBuffer {
6293
const lastCol = j === dataRowSize - 1;
6394
if (relOffset <= bitSkip && lastCol) {
6495
// no need to read new data
96+
6597
io.writeByte(byteB << relOffset);
6698
if ((bitSkip === 0 || bitSkip === relOffset) && !lastRow) {
6799
byteA = byteB;
@@ -70,6 +102,7 @@ class BMPEncoder extends IOBuffer {
70102
} else if (relOffset === 0) {
71103
byteA = byteB;
72104
byteB = this.readUint8();
105+
73106
io.writeByte(byteA);
74107
} else {
75108
byteA = byteB;
@@ -78,6 +111,7 @@ class BMPEncoder extends IOBuffer {
78111
((byteA << relOffset) & tableLeft[relOffset]) | (byteB >> iOffset)
79112
);
80113
}
114+
81115
if (lastCol) {
82116
offset += bitOverflow || 0;
83117
io.skip(skipSize);
@@ -101,10 +135,10 @@ class BMPEncoder extends IOBuffer {
101135
.writeUint32(0x00ffffff); // white
102136
}
103137

104-
writeBitmapFileHeader(imageOffset) {
138+
writeBitmapFileHeader(imageOffset: number, fileSize: number) {
105139
this.encoded
106140
.writeChars('BM') // 14 bytes bitmap file header
107-
.writeInt32(this.encoded.lastWrittenByte) // Size of BMP file in bytes
141+
.writeInt32(fileSize) // Size of BMP file in bytes
108142
.writeUint16(0)
109143
.writeUint16(0)
110144
.writeUint32(imageOffset);
@@ -120,22 +154,20 @@ class BMPEncoder extends IOBuffer {
120154
.writeInt32(this.height) // bV5Height
121155
.writeUint16(1) // bv5Planes - must be set to 1
122156
.writeUint16(this.bitDepth) // bV5BitCount
123-
.writeUint32(constants.BITMAPV5HEADER.Compression.BI_RGB) // bV5Compression - No compression
157+
.writeUint32(BITMAPV5HEADER.Compression.BI_RGB) // bV5Compression - No compression
124158
.writeUint32(totalBytes) // bv5SizeImage - size of pixel buffer (can be 0 if uncompressed)
125159
.writeInt32(0) // bV5XPelsPerMeter - resolution
126160
.writeInt32(0) // bV5YPelsPerMeter - resolution
127-
.writeUint32(Math.pow(2, this.bitDepth))
128-
.writeUint32(Math.pow(2, this.bitDepth))
161+
.writeUint32(2 ** this.bitDepth)
162+
.writeUint32(2 ** this.bitDepth)
129163
.writeUint32(0xff000000) // bV5RedMask
130164
.writeUint32(0x00ff0000) // bV5GreenMask
131165
.writeUint32(0x0000ff00) // bV5BlueMask
132166
.writeUint32(0x000000ff) // bV5AlphaMask
133-
.writeUint32(constants.BITMAPV5HEADER.LogicalColorSpace.LCS_sRGB)
167+
.writeUint32(BITMAPV5HEADER.LogicalColorSpace.LCS_sRGB)
134168
.skip(36) // bV5Endpoints
135169
.skip(12) // bV5GammaRed, Green, Blue
136-
.writeUint32(constants.BITMAPV5HEADER.GamutMappingIntent.LCS_GM_IMAGES)
170+
.writeUint32(BITMAPV5HEADER.GamutMappingIntent.LCS_GM_IMAGES)
137171
.skip(12); // ProfileData, ProfileSize, Reserved
138172
}
139173
}
140-
141-
module.exports = BMPEncoder;

src/__test__/bitDepth1.test.js renamed to src/__test__/bitDepth1.test.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
'use strict';
2-
3-
const testEncode = require('./testEncode');
1+
import { testEncode } from './testEncode';
42

53
const data = {
4+
width: 0,
5+
height: 0,
6+
data: new Uint8Array(),
67
bitDepth: 1,
78
components: 1,
89
channels: 1,
910
};
11+
1012
describe('encode image with bitDepth of 1', () => {
1113
it('encode a 5x5 image', () => {
1214
// 0 0 0 0 0
@@ -91,7 +93,6 @@ describe('encode image with bitDepth of 1', () => {
9193
]);
9294
testEncode(data, '42x2.bmp');
9395
});
94-
9596
it('encode image where skipBit can equal relOffset on the last column', () => {
9697
data.width = 60;
9798
data.height = 4;

src/__test__/errors.test.js renamed to src/__test__/errors.test.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
'use strict';
2-
3-
const encode = require('..').encode;
1+
import { encode } from '..';
42

53
describe('errors', () => {
64
it('should throw if width or height are undefined or 0', () => {
@@ -18,6 +16,7 @@ describe('errors', () => {
1816
expect(() => {
1917
encode({
2018
width: 10,
19+
height: 0,
2120
components: 1,
2221
bitDepth: 1,
2322
channels: 1,
Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
'use strict';
1+
import fs from 'node:fs';
2+
import path from 'node:path';
23

3-
const fs = require('fs');
4-
const path = require('path');
4+
import { encode } from '..';
5+
import type { DataToEncode } from '../BMPEncoder';
56

6-
const { expect } = require('@jest/globals');
7-
8-
const encode = require('..').encode;
9-
10-
module.exports = function testEncode(data, filename) {
7+
/**
8+
* Testing function for BMP encoding.
9+
* @param data - Data for encoding.
10+
* @param filename - Filename for a file to write.
11+
*/
12+
export function testEncode(data: DataToEncode, filename: string) {
1113
const buffer = encode(data);
1214
if (process.env.FAST_BMP_WRITE_DATA_FILES) {
1315
fs.writeFileSync(path.join(__dirname, 'files', filename), buffer);
@@ -16,4 +18,4 @@ module.exports = function testEncode(data, filename) {
1618
const fileDataUint8 = Uint8Array.from(fileData);
1719
expect(buffer).toStrictEqual(fileDataUint8);
1820
}
19-
};
21+
}

0 commit comments

Comments
 (0)