Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix missing types and jsDocs #89

Merged
merged 17 commits into from
May 12, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
"es2021": true,
"node": true
},
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
"parser": "@typescript-eslint/parser",
"extends": "standard",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npx tsc
- run: npm test
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@
"@types/content-type": "^1.1.8",
"@types/mocha": "latest",
"@types/readable-stream": "^4.0.11",
"@typescript-eslint/eslint-plugin": "^6.18.1",
"@typescript-eslint/parser": "^6.14.0",
"chai": "^5.0.3",
"eslint": "^8.56.0",
"eslint-config-standard": "^17.1.0",
Expand Down
52 changes: 0 additions & 52 deletions src/index.d.ts

This file was deleted.

12 changes: 4 additions & 8 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
import * as poParser from './poparser.js';
import { poParse, poStream } from './poparser.js';
import poCompiler from './pocompiler.js';
import moParser from './moparser.js';
import moCompiler from './mocompiler.js';

/**
* Translation parser and compiler for PO files
* @see https://www.gnu.org/software/gettext/manual/html_node/PO.html
*
* @type {import("./index.d.ts").po} po
*/
export const po = {
parse: poParser.parse,
createParseStream: poParser.stream,
parse: poParse,
createParseStream: poStream,
compile: poCompiler
};

/**
* Translation parser and compiler for PO files
* Translation parser and compiler for MO files
* @see https://www.gnu.org/software/gettext/manual/html_node/MO.html
*
* @type {import("./index.d.ts").mo} mo
*/
export const mo = {
parse: moParser,
Expand Down
124 changes: 71 additions & 53 deletions src/mocompiler.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import encoding from 'encoding';

Check failure on line 1 in src/mocompiler.js

View workflow job for this annotation

GitHub Actions / Node.js 18 on ubuntu-latest

Could not find a declaration file for module 'encoding'. '/home/runner/work/gettext-parser/gettext-parser/node_modules/encoding/lib/encoding.js' implicitly has an 'any' type.

Check failure on line 1 in src/mocompiler.js

View workflow job for this annotation

GitHub Actions / Node.js 18 on windows-latest

Could not find a declaration file for module 'encoding'. 'D:/a/gettext-parser/gettext-parser/node_modules/encoding/lib/encoding.js' implicitly has an 'any' type.

Check failure on line 1 in src/mocompiler.js

View workflow job for this annotation

GitHub Actions / Node.js 20 on ubuntu-latest

Could not find a declaration file for module 'encoding'. '/home/runner/work/gettext-parser/gettext-parser/node_modules/encoding/lib/encoding.js' implicitly has an 'any' type.

Check failure on line 1 in src/mocompiler.js

View workflow job for this annotation

GitHub Actions / Node.js 20 on windows-latest

Could not find a declaration file for module 'encoding'. 'D:/a/gettext-parser/gettext-parser/node_modules/encoding/lib/encoding.js' implicitly has an 'any' type.
import { HEADERS, formatCharset, generateHeader, compareMsgid } from './shared.js';
import contentType from 'content-type';

Expand All @@ -6,7 +6,7 @@
* Exposes general compiler function. Takes a translation
* object as a parameter and returns binary MO object
*
* @param {import('./index.d.ts').GetTextTranslations} table Translation object
* @param {import('./types.js').GetTextTranslations} table Translation object
erikyo marked this conversation as resolved.
Show resolved Hide resolved
* @return {Buffer} Compiled binary MO object
*/
export default function (table) {
Expand All @@ -16,37 +16,42 @@
}

/**
* Creates a MO compiler object.
*
* @constructor
* @param {import('./index.d.ts').GetTextTranslations} table Translation table as defined in the README
* @return {import('./index.d.ts').Compiler} Compiler
* Prepare the header object to be compatible with MO compiler
* @param {{[key: string]: string}} headers the headers
* @return {{[key: string]: string}} The prepared header
*/
function Compiler (table = {}) {
this._table = table;

let { headers = {}, translations = {} } = this._table;

headers = Object.keys(headers).reduce((result, key) => {
function prepareMoHeaders (headers) {
return Object.keys(headers).reduce((/** @type {{[key: string]: string}} */ result, key) => {
erikyo marked this conversation as resolved.
Show resolved Hide resolved
const lowerKey = key.toLowerCase();

if (HEADERS.has(lowerKey)) {
// POT-Creation-Date is removed in MO (see https://savannah.gnu.org/bugs/?49654)
if (lowerKey !== 'pot-creation-date') {
result[HEADERS.get(lowerKey)] = headers[key];
const value = HEADERS.get(lowerKey);
if (value) {
result[value] = headers[key];
}
}
} else {
result[key] = headers[key];
}

return result;
}, {});
}

// filter out empty translations
translations = Object.keys(translations).reduce((result, msgctxt) => {
/**
* Prepare the translation object to be compatible with MO compiler
* @param {import('./types.js').GetTextTranslations['translations']} translations
* @return {import('./types.js').GetTextTranslations['translations']}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you use the suggestion above, I would also declare the Translations type like this:

// At the top of the file with the other type import. We can use `GetTextTranslations` directly because it's already imported.
/**
 * @param {GetTextTranslations['translations']} Translations
 */
Suggested change
* @param {import('./types.js').GetTextTranslations['translations']} translations
* @return {import('./types.js').GetTextTranslations['translations']}
* @param {Translations} translations
* @return {Translations}

I haven't checked, though if Translations could be typed separately, rather than looking it up with key access on GetTextTranslations, that would be preferred. If that is acceptable then it would change to:

// At the top of the file with the other type import. We can use `GetTextTranslations` directly because it's already imported.
/**
 * @typedef {import('./types.js').Translations) Translations
 */

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes be patient but that is the fastest way with my IDE to generate jsdocs types. a 'cleaner' commit will follow all these changes

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i just give a try and seems that jsDocs only supports inline import :/

image

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks to me like you didn't import them. Is it okay for me to PR this branch so you can see what I mean?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure! I think you have even permission to edit my repo, if not tell me

*/
function prepareTranslations (translations) {
return Object.keys(translations).reduce((result, msgctxt) => {
const context = translations[msgctxt];
const msgs = Object.keys(context).reduce((result, msgid) => {
const hasTranslation = context[msgid].msgstr.some(item => !!item.length);
const msgs = Object.keys(context).reduce((/** @type {{[key: string]: string}} */ result, msgid) => {
/** @type {import('./types.js').GetTextTranslation[]} */
const TranslationMsgstr = context[msgid].msgstr;
const hasTranslation = TranslationMsgstr.some(item => !!item.length);

Check failure on line 54 in src/mocompiler.js

View workflow job for this annotation

GitHub Actions / Node.js 18 on ubuntu-latest

Property 'length' does not exist on type 'GetTextTranslation'.

Check failure on line 54 in src/mocompiler.js

View workflow job for this annotation

GitHub Actions / Node.js 18 on windows-latest

Property 'length' does not exist on type 'GetTextTranslation'.

Check failure on line 54 in src/mocompiler.js

View workflow job for this annotation

GitHub Actions / Node.js 20 on ubuntu-latest

Property 'length' does not exist on type 'GetTextTranslation'.

Check failure on line 54 in src/mocompiler.js

View workflow job for this annotation

GitHub Actions / Node.js 20 on windows-latest

Property 'length' does not exist on type 'GetTextTranslation'.

if (hasTranslation) {
result[msgid] = context[msgid];
Expand All @@ -56,26 +61,36 @@
}, {});

if (Object.keys(msgs).length) {
result[msgctxt] = msgs;

Check failure on line 64 in src/mocompiler.js

View workflow job for this annotation

GitHub Actions / Node.js 18 on ubuntu-latest

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.

Check failure on line 64 in src/mocompiler.js

View workflow job for this annotation

GitHub Actions / Node.js 18 on windows-latest

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.

Check failure on line 64 in src/mocompiler.js

View workflow job for this annotation

GitHub Actions / Node.js 20 on ubuntu-latest

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.

Check failure on line 64 in src/mocompiler.js

View workflow job for this annotation

GitHub Actions / Node.js 20 on windows-latest

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.
}

return result;
}, {});
}

this._table.translations = translations;
this._table.headers = headers;
/**
* Creates a MO compiler object.
*
* @param {import('./types.js').GetTextTranslations} [table] Translation table as defined in the README
*/
function Compiler (table) {
/** @type {import('./types.js').GetTextTranslations} _table The translation table */
this._table = {
charset: undefined,
translations: prepareTranslations(table?.translations ?? {}),
headers: prepareMoHeaders(table?.headers ?? {})
};

this._translations = [];

this._writeFunc = 'writeUInt32LE';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be removed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That assignment appears to have been established at some point, presumably intended for modification through options. However, with no implementation efforts undertaken, it remains as unused memory.

https://nodejs.org/api/buffer.html#bufwriteuint32levalue-offset

These serve as the 'placeholders' for the associated function name utilized for reading bytes from the po/mo files. Frankly, I'm uncertain whether it's worthwhile to implement the others, as file writing should adhere to a similar approach.

this made typing more complicated, what do you say I leave it or is it OK?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would argue that byte order should be an option of the compiler, so rather than remove the concept of changing the write function we should fix it.

If a mo file is parsed in a specific byte order it should be rewritten in the same order.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be an option of the compiler

i think this requires a separate issue. won't fix in this pr


this._handleCharset();
}

/**
* Magic bytes for the generated binary data
*/
Compiler.prototype.MAGIC = 0x950412de;
/**
* Magic bytes for the generated binary data
* @type {number} MAGIC file header magic value of mo file
*/
this.MAGIC = 0x950412de;
}

/**
* Handles header values, replaces or adds (if needed) a charset property
Expand All @@ -96,17 +111,19 @@

/**
* Generates an array of translation strings
* in the form of [{msgid:... , msgstr:...}]
* in the form of [{msgid:..., msgstr: ...}]
*
* @return {Array} Translation strings array
*/
Compiler.prototype._generateList = function () {
/** @type {import('./types.js').GetTextTranslation[]} */
const list = [];

list.push({
msgid: Buffer.alloc(0),
msgstr: encoding.convert(generateHeader(this._table.headers), this._table.charset)
});
if ('headers' in this._table) {
list.push({
msgid: Buffer.alloc(0),

Check failure on line 123 in src/mocompiler.js

View workflow job for this annotation

GitHub Actions / Node.js 18 on ubuntu-latest

Type 'Buffer' is not assignable to type 'string'.

Check failure on line 123 in src/mocompiler.js

View workflow job for this annotation

GitHub Actions / Node.js 18 on windows-latest

Type 'Buffer' is not assignable to type 'string'.

Check failure on line 123 in src/mocompiler.js

View workflow job for this annotation

GitHub Actions / Node.js 20 on ubuntu-latest

Type 'Buffer' is not assignable to type 'string'.

Check failure on line 123 in src/mocompiler.js

View workflow job for this annotation

GitHub Actions / Node.js 20 on windows-latest

Type 'Buffer' is not assignable to type 'string'.
msgstr: encoding.convert(generateHeader(this._table.headers), this._table.charset)
});
}

Object.keys(this._table.translations).forEach(msgctxt => {
if (typeof this._table.translations[msgctxt] !== 'object') {
Expand Down Expand Up @@ -148,20 +165,19 @@
/**
* Calculate buffer size for the final binary object
*
* @param {import('./index.d.ts').GetTextTranslations} list An array of translation strings from _generateList
* @return {Object} Size data of {msgid, msgstr, total}
* @param {import('./types.js').GetTextTranslation[]} list An array of translation strings from _generateList
* @return {{msgid: number, msgstr: number, total: number}} Size data of {msgid, msgstr, total}
erikyo marked this conversation as resolved.
Show resolved Hide resolved
*/
Compiler.prototype._calculateSize = function (list) {
let msgidLength = 0;
let msgstrLength = 0;
let totalLength = 0;

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

totalLength = 4 + // magic number
const totalLength = 4 + // magic number
4 + // revision
4 + // string count
4 + // original string table offset
Expand All @@ -183,52 +199,53 @@
/**
* Generates the binary MO object from the translation list
*
* @param {import('./index.d.ts').GetTextTranslations} list translation list
* @param {Object} size Byte size information
* @return {Buffer} Compiled MO object
* @param {import('./types.js').GetTextTranslation[]} list translation list
* @param {{ msgid: number; msgstr: number; total: number }} size Byte size information
* @return {Buffer} Compiled MO object
*/
Compiler.prototype._build = function (list, size) {
/** @type {Buffer} returnBuffer */
const returnBuffer = Buffer.alloc(size.total);
erikyo marked this conversation as resolved.
Show resolved Hide resolved
let curPosition = 0;
let i;
let len;

// magic
returnBuffer[this._writeFunc](this.MAGIC, 0);
returnBuffer.writeUInt32LE(this.MAGIC, 0);

// revision
returnBuffer[this._writeFunc](0, 4);
returnBuffer.writeUInt32LE(0, 4);

// string count
returnBuffer[this._writeFunc](list.length, 8);
returnBuffer.writeUInt32LE(list.length, 8);

// original string table offset
returnBuffer[this._writeFunc](28, 12);
returnBuffer.writeUInt32LE(28, 12);

// translation string table offset
returnBuffer[this._writeFunc](28 + (4 + 4) * list.length, 16);
returnBuffer.writeUInt32LE(28 + (4 + 4) * list.length, 16);

// hash table size
returnBuffer[this._writeFunc](0, 20);
returnBuffer.writeUInt32LE(0, 20);

// hash table offset
returnBuffer[this._writeFunc](28 + (4 + 4) * list.length * 2, 24);
returnBuffer.writeUInt32LE(28 + (4 + 4) * list.length * 2, 24);
erikyo marked this conversation as resolved.
Show resolved Hide resolved

// build originals table
// Build original table
curPosition = 28 + 2 * (4 + 4) * list.length;
for (i = 0, len = list.length; i < len; i++) {
list[i].msgid.copy(returnBuffer, curPosition);

Check failure on line 237 in src/mocompiler.js

View workflow job for this annotation

GitHub Actions / Node.js 18 on ubuntu-latest

Property 'copy' does not exist on type 'string'.

Check failure on line 237 in src/mocompiler.js

View workflow job for this annotation

GitHub Actions / Node.js 18 on windows-latest

Property 'copy' does not exist on type 'string'.

Check failure on line 237 in src/mocompiler.js

View workflow job for this annotation

GitHub Actions / Node.js 20 on ubuntu-latest

Property 'copy' does not exist on type 'string'.

Check failure on line 237 in src/mocompiler.js

View workflow job for this annotation

GitHub Actions / Node.js 20 on windows-latest

Property 'copy' does not exist on type 'string'.
returnBuffer[this._writeFunc](list[i].msgid.length, 28 + i * 8);
returnBuffer[this._writeFunc](curPosition, 28 + i * 8 + 4);
returnBuffer.writeUInt32LE(list[i].msgid.length, 28 + i * 8);
returnBuffer.writeUInt32LE(curPosition, 28 + i * 8 + 4);
returnBuffer[curPosition + list[i].msgid.length] = 0x00;
curPosition += list[i].msgid.length + 1;
}

// build translations table
// build translation table
for (i = 0, len = list.length; i < len; i++) {
list[i].msgstr.copy(returnBuffer, curPosition);

Check failure on line 246 in src/mocompiler.js

View workflow job for this annotation

GitHub Actions / Node.js 18 on ubuntu-latest

Property 'copy' does not exist on type 'string[]'.

Check failure on line 246 in src/mocompiler.js

View workflow job for this annotation

GitHub Actions / Node.js 18 on windows-latest

Property 'copy' does not exist on type 'string[]'.

Check failure on line 246 in src/mocompiler.js

View workflow job for this annotation

GitHub Actions / Node.js 20 on ubuntu-latest

Property 'copy' does not exist on type 'string[]'.

Check failure on line 246 in src/mocompiler.js

View workflow job for this annotation

GitHub Actions / Node.js 20 on windows-latest

Property 'copy' does not exist on type 'string[]'.
returnBuffer[this._writeFunc](list[i].msgstr.length, 28 + (4 + 4) * list.length + i * 8);
returnBuffer[this._writeFunc](curPosition, 28 + (4 + 4) * list.length + i * 8 + 4);
returnBuffer.writeUInt32LE(list[i].msgstr.length, 28 + (4 + 4) * list.length + i * 8);
returnBuffer.writeUInt32LE(curPosition, 28 + (4 + 4) * list.length + i * 8 + 4);
returnBuffer[curPosition + list[i].msgstr.length] = 0x00;
curPosition += list[i].msgstr.length + 1;
}
Expand All @@ -237,8 +254,9 @@
};

/**
* Compiles translation object into a binary MO object
* Compiles a translation object into a binary MO object
*
* @interface
* @return {Buffer} Compiled MO object
*/
Compiler.prototype.compile = function () {
Expand Down
Loading