Skip to content

Commit c40a44c

Browse files
erikyojohnhooks
andauthored
fix missing types and jsDocs (#89)
* fix missing types and jsDocs * removing definition file, the definitions were moved to the type.js file * enhanced type for pocompiler and postream * enhancing js docs types * jsdocs types (still few error to solve) * @johnhooks review suggestions Co-authored-by: John Hooks <bitmachina@outlook.com> * apply suggestion by johnhooks Co-authored-by: John Hooks <bitmachina@outlook.com> * apply @johnhooks suggestions 🙌 * wip types * fixed types * allows tsc to fail in ci tests * fix: adjust typing of the parsers and compilers This commit adds missing types and attempts fix type errors. There are still a few type errors, though how to fix them is not clear. Adds the `Translations` type for the `translations` property of the `GetTextTranslations` type. * add imports for types * add encoding declaration * add types directory to tsconfig include * remove types directory from .gitignore --------- Co-authored-by: John Hooks <bitmachina@outlook.com>
1 parent 955eaa2 commit c40a44c

13 files changed

Lines changed: 554 additions & 375 deletions

File tree

.eslintrc.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
"es2021": true,
44
"node": true
55
},
6-
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
7-
"parser": "@typescript-eslint/parser",
6+
"extends": "standard",
87
"parserOptions": {
98
"ecmaVersion": "latest",
109
"sourceType": "module"

.github/workflows/ci.yml

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,24 @@ on:
33
- push
44
- pull_request
55
jobs:
6+
build:
7+
name: Build with tsc
8+
runs-on: ubuntu-latest
9+
continue-on-error: true
10+
steps:
11+
- uses: actions/checkout@v4
12+
- uses: actions/setup-node@v4
13+
with:
14+
node-version: lts/*
15+
- run: npm install
16+
- run: npx tsc
617
test:
7-
name: Node.js ${{ matrix.node-version }} on ${{ matrix.os }}
18+
name: Test ${{ matrix.node-version }} on ${{ matrix.os }}
819
runs-on: ${{ matrix.os }}
920
strategy:
1021
fail-fast: false
1122
matrix:
12-
node-version:
13-
- 18
14-
- 20
23+
node: [ 18, 20 ]
1524
os:
1625
- ubuntu-latest
1726
- windows-latest

package.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@
3838
"@types/content-type": "^1.1.8",
3939
"@types/mocha": "latest",
4040
"@types/readable-stream": "^4.0.11",
41-
"@typescript-eslint/eslint-plugin": "^6.18.1",
42-
"@typescript-eslint/parser": "^6.14.0",
4341
"chai": "^5.0.3",
4442
"eslint": "^8.56.0",
4543
"eslint-config-standard": "^17.1.0",

src/index.d.ts

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

src/index.js

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,21 @@
1-
import * as poParser from './poparser.js';
1+
import { poParse, poStream } from './poparser.js';
22
import poCompiler from './pocompiler.js';
33
import moParser from './moparser.js';
44
import moCompiler from './mocompiler.js';
55

66
/**
77
* Translation parser and compiler for PO files
88
* @see https://www.gnu.org/software/gettext/manual/html_node/PO.html
9-
*
10-
* @type {import("./index.d.ts").po} po
119
*/
1210
export const po = {
13-
parse: poParser.parse,
14-
createParseStream: poParser.stream,
11+
parse: poParse,
12+
createParseStream: poStream,
1513
compile: poCompiler
1614
};
1715

1816
/**
19-
* Translation parser and compiler for PO files
17+
* Translation parser and compiler for MO files
2018
* @see https://www.gnu.org/software/gettext/manual/html_node/MO.html
21-
*
22-
* @type {import("./index.d.ts").mo} mo
2319
*/
2420
export const mo = {
2521
parse: moParser,

src/mocompiler.js

Lines changed: 92 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,30 @@ import encoding from 'encoding';
22
import { HEADERS, formatCharset, generateHeader, compareMsgid } from './shared.js';
33
import contentType from 'content-type';
44

5+
/**
6+
* @typedef {import('node:stream').Transform} Transform
7+
* @typedef {import('./types.js').GetTextTranslation} GetTextTranslation
8+
* @typedef {import('./types.js').GetTextTranslations} GetTextTranslations
9+
* @typedef {import('./types.js').Translations} Translations
10+
* @typedef {import('./types.js').WriteFunc} WriteFunc
11+
*/
12+
13+
/**
14+
* @typedef {Object} Size Data about the size of the compiled MO object.
15+
* @property {number} msgid The size of the msgid section.
16+
* @property {number} msgstr The size of the msgstr section.
17+
* @property {number} total The total size of the compiled MO object.
18+
*/
19+
20+
/**
21+
* @typedef {{ msgid: Buffer, msgstr: Buffer }} TranslationBuffers A translation object partially parsed.
22+
*/
23+
524
/**
625
* Exposes general compiler function. Takes a translation
726
* object as a parameter and returns binary MO object
827
*
9-
* @param {import('./index.d.ts').GetTextTranslations} table Translation object
28+
* @param {GetTextTranslations} table Translation object
1029
* @return {Buffer} Compiled binary MO object
1130
*/
1231
export default function (table) {
@@ -16,66 +35,85 @@ export default function (table) {
1635
}
1736

1837
/**
19-
* Creates a MO compiler object.
20-
*
21-
* @constructor
22-
* @param {import('./index.d.ts').GetTextTranslations} table Translation table as defined in the README
23-
* @return {import('./index.d.ts').Compiler} Compiler
38+
* Prepare the header object to be compatible with MO compiler
39+
* @param {Record<string, string>} headers the headers
40+
* @return {Record<string, string>} The prepared header
2441
*/
25-
function Compiler (table = {}) {
26-
this._table = table;
27-
28-
let { headers = {}, translations = {} } = this._table;
29-
30-
headers = Object.keys(headers).reduce((result, key) => {
42+
function prepareMoHeaders (headers) {
43+
return Object.keys(headers).reduce((result, key) => {
3144
const lowerKey = key.toLowerCase();
3245

3346
if (HEADERS.has(lowerKey)) {
3447
// POT-Creation-Date is removed in MO (see https://savannah.gnu.org/bugs/?49654)
3548
if (lowerKey !== 'pot-creation-date') {
36-
result[HEADERS.get(lowerKey)] = headers[key];
49+
const value = HEADERS.get(lowerKey);
50+
if (value) {
51+
result[value] = headers[key];
52+
}
3753
}
3854
} else {
3955
result[key] = headers[key];
4056
}
4157

4258
return result;
43-
}, {});
59+
}, /** @type {Record<string, string>} */ ({}));
60+
}
4461

45-
// filter out empty translations
46-
translations = Object.keys(translations).reduce((result, msgctxt) => {
62+
/**
63+
* Prepare the translation object to be compatible with MO compiler
64+
* @param {Translations} translations
65+
* @return {Translations}
66+
*/
67+
function prepareTranslations (translations) {
68+
return Object.keys(translations).reduce((result, msgctxt) => {
4769
const context = translations[msgctxt];
4870
const msgs = Object.keys(context).reduce((result, msgid) => {
49-
const hasTranslation = context[msgid].msgstr.some(item => !!item.length);
71+
const TranslationMsgstr = context[msgid].msgstr;
72+
const hasTranslation = TranslationMsgstr.some(item => !!item.length);
5073

5174
if (hasTranslation) {
5275
result[msgid] = context[msgid];
5376
}
5477

5578
return result;
56-
}, {});
79+
}, /** @type {Record<string, GetTextTranslation>} */({}));
5780

5881
if (Object.keys(msgs).length) {
5982
result[msgctxt] = msgs;
6083
}
6184

6285
return result;
63-
}, {});
86+
}, /** @type {Translations} */({}));
87+
}
6488

65-
this._table.translations = translations;
66-
this._table.headers = headers;
89+
/**
90+
* Creates a MO compiler object.
91+
* @this {Compiler & Transform}
92+
*
93+
* @param {GetTextTranslations} [table] Translation table as defined in the README
94+
*/
95+
function Compiler (table) {
96+
/** @type {GetTextTranslations} _table The translation table */
97+
this._table = {
98+
charset: undefined,
99+
translations: prepareTranslations(table?.translations ?? {}),
100+
headers: prepareMoHeaders(table?.headers ?? {})
101+
};
67102

68103
this._translations = [];
69-
104+
/**
105+
* @type {WriteFunc}
106+
*/
70107
this._writeFunc = 'writeUInt32LE';
71108

72109
this._handleCharset();
73-
}
74110

75-
/**
76-
* Magic bytes for the generated binary data
77-
*/
78-
Compiler.prototype.MAGIC = 0x950412de;
111+
/**
112+
* Magic bytes for the generated binary data
113+
* @type {number} MAGIC file header magic value of mo file
114+
*/
115+
this.MAGIC = 0x950412de;
116+
}
79117

80118
/**
81119
* Handles header values, replaces or adds (if needed) a charset property
@@ -96,17 +134,19 @@ Compiler.prototype._handleCharset = function () {
96134

97135
/**
98136
* Generates an array of translation strings
99-
* in the form of [{msgid:... , msgstr:...}]
137+
* in the form of [{msgid:..., msgstr: ...}]
100138
*
101-
* @return {Array} Translation strings array
102139
*/
103140
Compiler.prototype._generateList = function () {
141+
/** @type {TranslationBuffers[]} */
104142
const list = [];
105143

106-
list.push({
107-
msgid: Buffer.alloc(0),
108-
msgstr: encoding.convert(generateHeader(this._table.headers), this._table.charset)
109-
});
144+
if ('headers' in this._table) {
145+
list.push({
146+
msgid: Buffer.alloc(0),
147+
msgstr: encoding.convert(generateHeader(this._table.headers), this._table.charset)
148+
});
149+
}
110150

111151
Object.keys(this._table.translations).forEach(msgctxt => {
112152
if (typeof this._table.translations[msgctxt] !== 'object') {
@@ -133,7 +173,7 @@ Compiler.prototype._generateList = function () {
133173
key += '\u0000' + msgidPlural;
134174
}
135175

136-
const value = [].concat(this._table.translations[msgctxt][msgid].msgstr || []).join('\u0000');
176+
const value = /** @type {string[]} */([]).concat(this._table.translations[msgctxt][msgid].msgstr ?? []).join('\u0000');
137177

138178
list.push({
139179
msgid: encoding.convert(key, this._table.charset),
@@ -148,20 +188,19 @@ Compiler.prototype._generateList = function () {
148188
/**
149189
* Calculate buffer size for the final binary object
150190
*
151-
* @param {import('./index.d.ts').GetTextTranslations} list An array of translation strings from _generateList
152-
* @return {Object} Size data of {msgid, msgstr, total}
191+
* @param {TranslationBuffers[]} list An array of translation strings from _generateList
192+
* @return {Size} Size data of {msgid, msgstr, total}
153193
*/
154194
Compiler.prototype._calculateSize = function (list) {
155195
let msgidLength = 0;
156196
let msgstrLength = 0;
157-
let totalLength = 0;
158197

159198
list.forEach(translation => {
160199
msgidLength += translation.msgid.length + 1; // + extra 0x00
161200
msgstrLength += translation.msgstr.length + 1; // + extra 0x00
162201
});
163202

164-
totalLength = 4 + // magic number
203+
const totalLength = 4 + // magic number
165204
4 + // revision
166205
4 + // string count
167206
4 + // original string table offset
@@ -183,9 +222,9 @@ Compiler.prototype._calculateSize = function (list) {
183222
/**
184223
* Generates the binary MO object from the translation list
185224
*
186-
* @param {import('./index.d.ts').GetTextTranslations} list translation list
187-
* @param {Object} size Byte size information
188-
* @return {Buffer} Compiled MO object
225+
* @param {TranslationBuffers[]} list translation list
226+
* @param {Size} size Byte size information
227+
* @return {Buffer} Compiled MO object
189228
*/
190229
Compiler.prototype._build = function (list, size) {
191230
const returnBuffer = Buffer.alloc(size.total);
@@ -214,21 +253,23 @@ Compiler.prototype._build = function (list, size) {
214253
// hash table offset
215254
returnBuffer[this._writeFunc](28 + (4 + 4) * list.length * 2, 24);
216255

217-
// build originals table
256+
// Build original table
218257
curPosition = 28 + 2 * (4 + 4) * list.length;
219258
for (i = 0, len = list.length; i < len; i++) {
220-
list[i].msgid.copy(returnBuffer, curPosition);
221-
returnBuffer[this._writeFunc](list[i].msgid.length, 28 + i * 8);
222-
returnBuffer[this._writeFunc](curPosition, 28 + i * 8 + 4);
259+
const msgidLength = /** @type {Buffer} */(/** @type {unknown} */(list[i].msgid));
260+
msgidLength.copy(returnBuffer, curPosition);
261+
returnBuffer.writeUInt32LE(list[i].msgid.length, 28 + i * 8);
262+
returnBuffer.writeUInt32LE(curPosition, 28 + i * 8 + 4);
223263
returnBuffer[curPosition + list[i].msgid.length] = 0x00;
224264
curPosition += list[i].msgid.length + 1;
225265
}
226266

227-
// build translations table
267+
// build translation table
228268
for (i = 0, len = list.length; i < len; i++) {
229-
list[i].msgstr.copy(returnBuffer, curPosition);
230-
returnBuffer[this._writeFunc](list[i].msgstr.length, 28 + (4 + 4) * list.length + i * 8);
231-
returnBuffer[this._writeFunc](curPosition, 28 + (4 + 4) * list.length + i * 8 + 4);
269+
const msgstrLength = /** @type {Buffer} */(/** @type {unknown} */(list[i].msgstr));
270+
msgstrLength.copy(returnBuffer, curPosition);
271+
returnBuffer.writeUInt32LE(list[i].msgstr.length, 28 + (4 + 4) * list.length + i * 8);
272+
returnBuffer.writeUInt32LE(curPosition, 28 + (4 + 4) * list.length + i * 8 + 4);
232273
returnBuffer[curPosition + list[i].msgstr.length] = 0x00;
233274
curPosition += list[i].msgstr.length + 1;
234275
}
@@ -237,8 +278,9 @@ Compiler.prototype._build = function (list, size) {
237278
};
238279

239280
/**
240-
* Compiles translation object into a binary MO object
281+
* Compiles a translation object into a binary MO object
241282
*
283+
* @interface
242284
* @return {Buffer} Compiled MO object
243285
*/
244286
Compiler.prototype.compile = function () {

0 commit comments

Comments
 (0)