diff --git a/.eslintrc b/.eslintrc index 18aacdab..807816fb 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,7 +1,22 @@ { - "extends": [ - "oclif", - "oclif-typescript", - "prettier" - ] + "extends": ["oclif", "oclif-typescript", "prettier"], + "rules": { + "unicorn/prefer-node-protocol": "off", + "node/no-extraneous-import": "off", + "unicorn/prefer-module": "off", + "unicorn/import-style": [ + "error", + { + "styles": { + "path": { + "namespace": true + } + } + } + ], + "no-warning-comments": [ + "warn", + { "terms": ["fixme", "xxx"], "location": "start" } + ] + } } diff --git a/package.json b/package.json index 36f3d9ed..b3131db4 100644 --- a/package.json +++ b/package.json @@ -20,14 +20,12 @@ "update-gitmoji-config": "node ./scripts/update-gitmoji-config.js" }, "devDependencies": { - "@types/jest-when": "^2.7.2", "chai": "^4.2.0", "eslint": "^7.32.0", "eslint-config-oclif": "^4.0.0", "eslint-config-oclif-typescript": "^1.0.2", "eslint-config-prettier": "^8.3.0", "husky": "^4.3.0", - "jest-when": "^3.0.1", "lint-staged": "^10.5.1", "prettier": "^2.1.1", "typescript": "^3.9" diff --git a/packages/cli/package.json b/packages/cli/package.json index e97d6627..6e6a4e58 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -36,7 +36,9 @@ "devDependencies": { "@oclif/dev-cli": "^1", "@oclif/test": "^1", - "@types/node": "^10" + "@types/node": "^10", + "@types/jest-when": "^2.7.2", + "jest-when": "^3.0.1" }, "engines": { "node": ">=8.0.0" diff --git a/packages/cli/src/base.ts b/packages/cli/src/base.ts index cd7c4295..94472b0c 100644 --- a/packages/cli/src/base.ts +++ b/packages/cli/src/base.ts @@ -8,7 +8,7 @@ const pkg = require("../package.json"); export default abstract class Base extends Command { protected conf: undefined | null | ConfigType; - async init() { + async init(): Promise { const notifier = updateNotifier({ pkg, updateCheckInterval: 1000, diff --git a/packages/cli/src/commands/__tests__/update.test.ts b/packages/cli/src/commands/__tests__/update.test.ts index e17c1ae4..57c67aca 100644 --- a/packages/cli/src/commands/__tests__/update.test.ts +++ b/packages/cli/src/commands/__tests__/update.test.ts @@ -78,7 +78,6 @@ describe("update command", () => { const mockSheetLines3 = [{ key: "sheet3.line_1" }, { key: "sheet3.line_2" }]; const test = oclifTest.register("setupMocks", () => ({ - /* eslint-disable no-console */ run() { ReaderMock.mockClear(); WorksheetReaderMock.mockClear(); diff --git a/packages/cli/src/commands/open.ts b/packages/cli/src/commands/open.ts index 5e11d630..a97741d8 100644 --- a/packages/cli/src/commands/open.ts +++ b/packages/cli/src/commands/open.ts @@ -17,7 +17,7 @@ class Open extends Base { id: cliFlags.id.flag(), }; - async run() { + async run(): Promise { const { flags } = this.parse(Open); const sheetId = flags.id; diff --git a/packages/cli/src/commands/update.ts b/packages/cli/src/commands/update.ts index c66ff241..1b4860f5 100644 --- a/packages/cli/src/commands/update.ts +++ b/packages/cli/src/commands/update.ts @@ -90,26 +90,23 @@ class Update extends Base { const allWorksheetLines = Object.values(linesByWorkshet).flat(); const OTHER_DOMAIN = "__other__"; - const linesByDomain = allWorksheetLines.reduce( - (domainLines: { [domain: string]: Line[] }, line: Line) => { - const domain = - domains.find((d) => line.key.startsWith(`${d}.`)) || OTHER_DOMAIN; + const linesByDomain: { [domain: string]: Line[] } = {}; - domainLines[domain] = domainLines[domain] ?? []; - domainLines[domain].push(line); + for (const line of allWorksheetLines) { + const domain = + domains.find((d) => line.key.startsWith(`${d}.`)) || OTHER_DOMAIN; - return domainLines; - }, - {} - ); + linesByDomain[domain] = linesByDomain[domain] ?? []; + linesByDomain[domain].push(line); + } - domains - .filter((domain) => linesByDomain[domain].length === 0) - .forEach((domain) => { + for (const domain of domains) { + if (linesByDomain[domain].length === 0) { this.warn( `😐 Received no lines for language ${langName} and domain ${domain}` ); - }); + } + } return ( Object.keys(linesByDomain) @@ -128,7 +125,7 @@ class Update extends Base { } // eslint-disable-next-line complexity - async run() { + async run(): Promise { const { flags } = this.parse(Update); const sheetId = flags.id ?? ""; @@ -161,14 +158,19 @@ class Update extends Base { try { worksheetReader = new WorksheetReader(sheets, { logger }); } catch (error) { - if (error instanceof InvalidFilterError) { - throw new IncorrectFlagValue(error.message); - } else { - throw error; - } + const normalizedError = + error instanceof InvalidFilterError + ? new IncorrectFlagValue(error.message) + : error; + + throw normalizedError; } - const plugins = loadPlugins(this.conf?.plugins, { logger }, { languages }); + const plugins = loadPlugins( + this.conf?.plugins ?? [], + { logger }, + { languages } + ); const outputTransformer = transformersByFormat[format]; const reader = new Reader(sheetId, worksheetReader, plugins, { logger, diff --git a/packages/cli/src/flags/id.ts b/packages/cli/src/flags/id.ts index 1a95b191..d452e648 100644 --- a/packages/cli/src/flags/id.ts +++ b/packages/cli/src/flags/id.ts @@ -14,7 +14,7 @@ export const flag = flags.build({ }, }); -export const invariant = (id?: string) => { +export const invariant = (id?: string): void => { if (!id) { throw new MissingFlagValue("Sheet id"); } diff --git a/packages/cli/src/invariants.ts b/packages/cli/src/invariants.ts index c557357c..f7c6dd57 100644 --- a/packages/cli/src/invariants.ts +++ b/packages/cli/src/invariants.ts @@ -1,14 +1,16 @@ import { CLIError } from "@oclif/errors"; export const cliInvariant = ( - expression: any, + expression: unknown, message: string, - options: object = {} -) => { + options: Record = {} +): void => { if (!expression) { throw new CLIError(message, options); } }; -export const noExitCliInvariant = (expression: any, message: string) => - cliInvariant(expression, message, { exit: false }); +export const noExitCliInvariant = ( + expression: unknown, + message: string +): void => cliInvariant(expression, message, { exit: false }); diff --git a/packages/core/src/__tests__/writer.test.ts b/packages/core/src/__tests__/writer.test.ts index a7e3a2b0..a793b114 100644 --- a/packages/core/src/__tests__/writer.test.ts +++ b/packages/core/src/__tests__/writer.test.ts @@ -2,9 +2,9 @@ import { EOL } from "os"; import * as mkdirp from "mkdirp"; const fs = { - accessAsync: jest.fn().mockResolvedValue(undefined), + accessAsync: jest.fn(), readFileAsync: jest.fn().mockResolvedValue(""), - writeFileAsync: jest.fn().mockResolvedValue(undefined), + writeFileAsync: jest.fn(), }; jest.mock("fs"); @@ -29,6 +29,7 @@ const plugin = { pluginName: "test", transformFullOutput: jest.fn(), transformLine: jest.fn(), + readTranslation: jest.fn(), }; const plugins = new PluginsRunner([plugin], { logger }); diff --git a/packages/core/src/line.ts b/packages/core/src/line.ts index c03f9d9c..09c63fa6 100644 --- a/packages/core/src/line.ts +++ b/packages/core/src/line.ts @@ -1,7 +1,6 @@ -/* eslint-disable unicorn/filename-case */ const COMMENT_STARTERS = ["//", "#"]; -const PLURAL_KEY_RE = /.+##\{(zero|one|two|few|many|other)\}/; -const PLURAL_POSTFIX_RE = /##\{(zero|one|two|few|many|other)\}/; +const PLURAL_KEY_RE = /.+##{(zero|one|two|few|many|other)}/; +const PLURAL_POSTFIX_RE = /##{(zero|one|two|few|many|other)}/; enum Type { COMMENT, @@ -45,71 +44,72 @@ class Line { this.value = value ?? ""; } - static checkIsComment(val: any): boolean { + static checkIsComment(val: string): boolean { for (const commentStarter of COMMENT_STARTERS) { if (val.indexOf(commentStarter) === 0) { return true; } } + return false; } - static checkIsPlural = function (val: any): boolean { - if (val.match(PLURAL_KEY_RE)) { + static checkIsPlural = function (val: string): boolean { + if (PLURAL_KEY_RE.test(val)) { return true; } + return false; }; - static parseKeysFromPlural = function (val: string) { + static parseKeysFromPlural = function (val: string): [string, string] { const match = val.match(PLURAL_POSTFIX_RE); if (match) { return [val.replace(match[0], ""), match[1]]; } + return [val, ""]; }; - static normalizeComment(val: string) { + static normalizeComment(val: string): string { for (const commentStarter of COMMENT_STARTERS) { const index = val.indexOf(commentStarter); if (index === 0) { - const normalized = val.substr( - commentStarter.length, - val.length - commentStarter.length - ); + const normalized = val.slice(commentStarter.length); return normalized.trim(); } } + return val; } - isEmpty() { + isEmpty(): boolean { return !this.isComment() && !(this.key && this.value); } - isComment() { + isComment(): boolean { return this.type === Type.COMMENT; } - isPlural() { + isPlural(): boolean { return this.type === Type.PLURAL; } - getComment() { + getComment(): string { return this.key; } - getPluralKey() { + getPluralKey(): string { return this.pluralKey; } - setKey(nextKey: string | ((key: string) => string)) { + setKey(nextKey: string | ((key: string) => string)): void { this.key = typeof nextKey === "function" ? nextKey(this.key) : nextKey; } - setValue(nextValue: string | ((value: string) => string)) { + setValue(nextValue: string | ((value: string) => string)): void { this.value = typeof nextValue === "function" ? nextValue(this.value) : nextValue; } diff --git a/packages/core/src/plugins/create.ts b/packages/core/src/plugins/create.ts index 0061b80d..cb441063 100644 --- a/packages/core/src/plugins/create.ts +++ b/packages/core/src/plugins/create.ts @@ -57,7 +57,7 @@ const pluginDefaults: LoksePlugin = { readTranslation: identity, }; -export function createPlugin(plugin: Partial) { +export function createPlugin(plugin: Partial): LoksePlugin { return { ...pluginDefaults, ...plugin, diff --git a/packages/core/src/plugins/load.ts b/packages/core/src/plugins/load.ts index 16df9a96..589aa97a 100644 --- a/packages/core/src/plugins/load.ts +++ b/packages/core/src/plugins/load.ts @@ -52,14 +52,14 @@ function loadPlugin( export type PluginName = string; export interface PluginDefinition { name: PluginName; - options: object; + options: Record; } export function loadPlugins( - plugins: (PluginName | PluginDefinition)[] | unknown = [], + plugins: (PluginName | PluginDefinition)[] | unknown, options: GeneralPluginOptions, meta: GeneralPluginMeta -) { +): PluginsRunner { let loadedPlugins: NamedLoksePlugin[]; if (Array.isArray(plugins)) { diff --git a/packages/core/src/reader/__tests__/spreadsheet-reader.test.ts b/packages/core/src/reader/__tests__/spreadsheet-reader.test.ts index 1ff36a16..9c5c864b 100644 --- a/packages/core/src/reader/__tests__/spreadsheet-reader.test.ts +++ b/packages/core/src/reader/__tests__/spreadsheet-reader.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable camelcase */ import { GoogleSpreadsheet } from "google-spreadsheet"; import SpreadsheetReader from "../spreadsheet-reader"; import WorksheetReader from "../worksheet-reader"; @@ -14,6 +15,18 @@ const GoogleSpreadsheetMock = GoogleSpreadsheet as jest.Mock; jest.mock("google-spreadsheet"); +const makeFakeLine = (id: string) => { + return { id: `line_${id}` } as unknown as Line; +}; + +const makeFakeWorksheet = (title: string, lines: Line[]) => { + const fakeWorksheet = { + title, + extractLines: jest.fn().mockReturnValue(lines), + }; + return fakeWorksheet as unknown as Worksheet; +}; + describe("SpreadsheetReader", () => { const testLogger = { log: jest.fn(), @@ -90,18 +103,6 @@ describe("SpreadsheetReader", () => { }); describe("read", () => { - const makeFakeLine = (id: string) => { - return { id: `line_${id}` } as unknown as Line; - }; - - const makeFakeWorksheet = (title: string, lines: Line[]) => { - const fakeWorksheet = { - title, - extractLines: jest.fn().mockReturnValue(lines), - }; - return fakeWorksheet as unknown as Worksheet; - }; - const linesSet1 = [makeFakeLine("1_1"), makeFakeLine("1_2")]; const linesSet2 = [ @@ -204,7 +205,7 @@ describe("SpreadsheetReader", () => { jest.spyOn(reader, "fetchSheets").mockResolvedValue(sheetsList); await expect(reader.read("key", "en-gb")).resolves.toEqual({ - fakeSheet1: linesSet1.concat(linesSet3), + fakeSheet1: [...linesSet1, ...linesSet3], fakeSheet2: linesSet2, }); expect(testLogger.warn).toHaveBeenCalledTimes(1); diff --git a/packages/core/src/reader/__tests__/worksheet.test.ts b/packages/core/src/reader/__tests__/worksheet.test.ts index 3a2be6cc..5d7bb75f 100644 --- a/packages/core/src/reader/__tests__/worksheet.test.ts +++ b/packages/core/src/reader/__tests__/worksheet.test.ts @@ -3,32 +3,32 @@ import { KeyColumnNotFound, LangColumnNotFound } from "../../errors"; import { PluginsRunner } from "../../plugins"; import Worksheet from "../worksheet"; +const createRow = (rowIndex: number, values: { [key: string]: any }) => + ({ + rowIndex, + ...values, + save: () => null, + delete: () => null, + } as unknown as GoogleSpreadsheetRow); + describe("Worksheet", () => { const logger = { warn: jest.fn(), log: jest.fn() }; const noPlugins = new PluginsRunner([], { logger }); - const createRow = (rowIndex: number, values: { [key: string]: any }) => - ({ - rowIndex, - ...values, - save: () => null, - delete: () => null, - } as unknown as GoogleSpreadsheetRow); - it("should extract lines", async () => { const worksheet = new Worksheet( "Worksheet1", - ["Key", "Value_fr", "Value_nl"], + ["Key", "Value-fr", "Value-nl"], [ createRow(1, { Key: "MaClĂ©1", - Value_fr: "La valeur 1", - Value_nl: "De valuue 1", + "Value-fr": "La valeur 1", + "Value-nl": "De valuue 1", }), createRow(2, { Key: "MaClĂ©2", - Value_fr: "La vale de la clĂ© 2", - Value_nl: "De valuee van key 2", + "Value-fr": "La vale de la clĂ© 2", + "Value-nl": "De valuee van key 2", }), createRow(3, { Key: "// un commentaire" }), createRow(4, { Key: "une clĂ©e" }), @@ -37,7 +37,7 @@ describe("Worksheet", () => { ] ); - const lines = await worksheet.extractLines("Key", "Value_fr", noPlugins); + const lines = await worksheet.extractLines("Key", "Value-fr", noPlugins); expect(lines.length).toEqual(6); expect(lines[0].key).toEqual("MaClĂ©1"); @@ -50,30 +50,30 @@ describe("Worksheet", () => { it("should throw when key column doesnt exist ", async () => { const worksheet = new Worksheet( "Worksheet2", - ["Key", "Value_fr", "Value_nl"], + ["Key", "Value-fr", "Value-nl"], [ createRow(1, { Key: "MaClĂ©1", - Value_fr: "La valeur 1", - Value_nl: "De valuue 1", + "Value-fr": "La valeur 1", + "Value-nl": "De valuue 1", }), ] ); await expect(() => - worksheet.extractLines("Wrong_Key", "Value_fr", noPlugins) + worksheet.extractLines("Wrong_Key", "Value-fr", noPlugins) ).rejects.toThrow(new KeyColumnNotFound("Wrong_Key", "Worksheet2").message); }); it("should throw when lang column doesnt exist ", async () => { const worksheet = new Worksheet( "Worksheet2", - ["Key", "Value_fr", "Value_nl"], + ["Key", "Value-fr", "Value-nl"], [ createRow(1, { Key: "MaClĂ©1", - Value_fr: "La valeur 1", - Value_nl: "De valuue 1", + "Value-fr": "La valeur 1", + "Value-nl": "De valuue 1", }), ] ); @@ -86,18 +86,18 @@ describe("Worksheet", () => { it("should keep empty lines", async () => { const worksheet = new Worksheet( "Worksheet3", - ["Key", "Value_fr", "Value_nl"], + ["Key", "Value-fr", "Value-nl"], [ createRow(1, {}), createRow(2, { Key: "MaClĂ©1", - Value_fr: "La valeur 1", - Value_nl: "De valuue 1", + "Value-fr": "La valeur 1", + "Value-nl": "De valuue 1", }), ] ); - const result = await worksheet.extractLines("Key", "Value_fr", noPlugins); + const result = await worksheet.extractLines("Key", "Value-fr", noPlugins); expect(result.length).toEqual(2); expect(result[0].isEmpty()).toEqual(true); @@ -107,11 +107,11 @@ describe("Worksheet", () => { it("should match column names case insensitively", async () => { let worksheet = new Worksheet( "Worksheet4", - ["Key", "Value_FR"], - [createRow(1, { Key: "MaClĂ©1", Value_FR: "La valeur 1" })] + ["Key", "Value-FR"], + [createRow(1, { Key: "MaClĂ©1", "Value-FR": "La valeur 1" })] ); - let result = await worksheet.extractLines("key", "value_fr", noPlugins); + let result = await worksheet.extractLines("key", "value-fr", noPlugins); expect(result.length).toEqual(1); expect(result[0].key).toEqual("MaClĂ©1"); @@ -119,11 +119,11 @@ describe("Worksheet", () => { worksheet = new Worksheet( "Worksheet5", - ["key", "value_fr"], - [createRow(1, { key: "MaClĂ©2", value_fr: "La valeur 2" })] + ["key", "value-fr"], + [createRow(1, { key: "MaClĂ©2", "value-fr": "La valeur 2" })] ); - result = await worksheet.extractLines("Key", "VALUE_FR", noPlugins); + result = await worksheet.extractLines("Key", "VALUE-FR", noPlugins); expect(result.length).toEqual(1); expect(result[0].key).toEqual("MaClĂ©2"); diff --git a/packages/core/src/reader/spreadsheet-reader.ts b/packages/core/src/reader/spreadsheet-reader.ts index 47c999fd..cfdb1d2b 100644 --- a/packages/core/src/reader/spreadsheet-reader.ts +++ b/packages/core/src/reader/spreadsheet-reader.ts @@ -35,14 +35,16 @@ export class SpreadsheetReader { this.worksheets = null; } - async authenticate() { + async authenticate(): Promise { const { LOKSE_API_KEY, LOKSE_SERVICE_ACCOUNT_EMAIL, LOKSE_PRIVATE_KEY } = process.env; if (LOKSE_SERVICE_ACCOUNT_EMAIL && LOKSE_PRIVATE_KEY) { await this.spreadsheet.useServiceAccountAuth({ + // eslint-disable-next-line camelcase client_email: LOKSE_SERVICE_ACCOUNT_EMAIL, // Treat new lines properly - https://stackoverflow.com/a/36439803/7051731 + // eslint-disable-next-line camelcase private_key: LOKSE_PRIVATE_KEY.replace(/\\n/g, "\n"), }); } else if (LOKSE_API_KEY) { @@ -52,7 +54,7 @@ export class SpreadsheetReader { } } - async fetchSheets() { + async fetchSheets(): Promise { if (!this.worksheets) { await this.authenticate(); @@ -68,44 +70,39 @@ export class SpreadsheetReader { return this.worksheets; } - async read(keyColumn: string, valueColumn: string) { + async read( + keyColumn: string, + valueColumn: string + ): Promise { const worksheets = await this.fetchSheets(); const plugins = this.plugins; - const worksheetsLines = await worksheets.reduce< - Promise - >( - async ( - worksheetLinesPromise: Promise, - worksheet - ) => { - const worksheetLines = await worksheetLinesPromise; - const { title } = worksheet; - - try { - const lines = await worksheet.extractLines( - keyColumn, - valueColumn, - plugins + const worksheetsLines: WorksheetLinesByTitle = {}; + const processWorksheetsPromises = worksheets.map(async (worksheet) => { + const { title } = worksheet; + + try { + const lines = await worksheet.extractLines( + keyColumn, + valueColumn, + plugins + ); + + if (worksheetsLines[title]) { + this.logger.warn( + `🔀 Found two sheets with same title ${title}. We're gonna concat the data.` ); - if (worksheetLines[title]) { - this.logger.warn( - `🔀 Found two sheets with same title ${title}. We're gonna concat the data.` - ); - - worksheetLines[title] = worksheetLines[title].concat(lines); - } else { - worksheetLines[title] = lines; - } - } catch (error) { - this.logger.warn(error.message); + worksheetsLines[title] = [...worksheetsLines[title], ...lines]; + } else { + worksheetsLines[title] = lines; } + } catch (error) { + this.logger.warn(error.message); + } + }); - return worksheetLines; - }, - Promise.resolve({}) - ); + await Promise.all(processWorksheetsPromises); return worksheetsLines; } diff --git a/packages/core/src/reader/worksheet-reader.ts b/packages/core/src/reader/worksheet-reader.ts index b124a95e..a7df3714 100644 --- a/packages/core/src/reader/worksheet-reader.ts +++ b/packages/core/src/reader/worksheet-reader.ts @@ -12,7 +12,7 @@ import type { Logger } from "../logger"; export class InvalidFilterError extends Error { public filterStringified: string; - constructor(filter: any) { + constructor(filter: unknown) { const filterStringified = JSON.stringify(filter); super( `đŸ’„ Invalid sheets filter provided: ${filterStringified}. Look at the supported filter format reference.` @@ -89,7 +89,7 @@ class WorksheetReader { static isSheetInTheList( worksheet: GoogleSpreadsheetWorksheet, list: SheetIndexOrTitle[] - ) { + ): boolean { return list.some((sheetFilter: string | number) => { if (sheetFilter === WorksheetReader.ALL_SHEETS_FILTER) { return true; @@ -119,13 +119,13 @@ class WorksheetReader { return isIncluded && !isExcluded; } - async loadSheet(worksheet: GoogleSpreadsheetWorksheet) { + async loadSheet(worksheet: GoogleSpreadsheetWorksheet): Promise { const rows = await worksheet.getRows(); return new Worksheet(worksheet.title, worksheet.headerValues, rows); } - async read(spreadsheet: GoogleSpreadsheet) { + async read(spreadsheet: GoogleSpreadsheet): Promise { const worksheets = spreadsheet.sheetsByIndex.filter((worksheet) => this.shouldUseWorksheet(worksheet) ); @@ -148,7 +148,9 @@ class WorksheetReader { this.logger.warn(`${message}. `); } - const sheets = await all(worksheets.map(this.loadSheet)); + const sheets = await all( + worksheets.map((worksheet) => this.loadSheet(worksheet)) + ); return sheets; } diff --git a/packages/core/src/reader/worksheet.ts b/packages/core/src/reader/worksheet.ts index d4ea049f..c2c15fb8 100644 --- a/packages/core/src/reader/worksheet.ts +++ b/packages/core/src/reader/worksheet.ts @@ -22,7 +22,7 @@ export default class Worksheet { keyColumn: string, langColumn: string, plugins: PluginsRunner - ) { + ): Promise { let keyColumnId = ""; let langColumnId = ""; @@ -30,6 +30,7 @@ export default class Worksheet { if (isEqualCaseInsensitive(headerKey, keyColumn)) { keyColumnId = headerKey; } + if (isEqualCaseInsensitive(headerKey, langColumn)) { langColumnId = headerKey; } diff --git a/packages/core/src/transformer/android.ts b/packages/core/src/transformer/android.ts index be80d40e..ffdbfc55 100644 --- a/packages/core/src/transformer/android.ts +++ b/packages/core/src/transformer/android.ts @@ -4,7 +4,7 @@ import { Transformer } from "./transformer"; function setCharAt(str: string, index: number, chr: number) { if (index > str.length - 1) return str; - return str.substr(0, index) + chr + str.substr(index + 1); + return str.slice(0, index) + chr + str.slice(index + 1); } const androidTransformer: Transformer = { @@ -15,7 +15,7 @@ const androidTransformer: Transformer = { transformKeyValue(key, value) { let normalizedValue = value.replace(/%newline%/gi, "\\n"); normalizedValue = normalizedValue.replace(/'/gi, "\\'"); - normalizedValue = normalizedValue.replace(/%([sdf])/gi, "%#$$$1"); + normalizedValue = normalizedValue.replace(/%([dfs])/gi, "%#$$$1"); normalizedValue = normalizedValue.replace(/&/gi, "&"); normalizedValue = normalizedValue.replace(/\u00A0/gi, "\\u00A0"); normalizedValue = normalizedValue.replace( @@ -40,10 +40,10 @@ const androidTransformer: Transformer = { transformPluralsValues(key, values) { let ouput = ' ' + EOL; - for (let i = 0; i < values.length; i++) { - let normalizedValue = values[i].value.replace(/%newline%/gi, "\\n"); + for (const value of values) { + let normalizedValue = value.value.replace(/%newline%/gi, "\\n"); normalizedValue = normalizedValue.replace(/'/gi, "\\'"); - normalizedValue = normalizedValue.replace(/%([sdf])/gi, "%#$$$1"); + normalizedValue = normalizedValue.replace(/%([dfs])/gi, "%#$$$1"); normalizedValue = normalizedValue.replace(/&/gi, "&"); normalizedValue = normalizedValue.replace(/\u00A0/gi, "\\u00A0"); normalizedValue = normalizedValue.replace( @@ -53,7 +53,7 @@ const androidTransformer: Transformer = { ouput += ' ' + normalizedValue + "" + @@ -88,11 +88,10 @@ const androidTransformer: Transformer = { '' + EOL + "" + EOL; } else { const autoGeneratedIndex = input.indexOf(AUTOGENERATED_TAG as string); - if (autoGeneratedIndex >= 0) { - output = input.substr(0, autoGeneratedIndex); - } else { - output = input.substr(0, closeTagIndex); - } + output = input.slice( + 0, + autoGeneratedIndex >= 0 ? autoGeneratedIndex : closeTagIndex + ); } output += AUTOGENERATED_TAG + EOL + newValues + EOL + ""; diff --git a/packages/core/src/transformer/ios.ts b/packages/core/src/transformer/ios.ts index fe13bf8a..fd93ffdd 100644 --- a/packages/core/src/transformer/ios.ts +++ b/packages/core/src/transformer/ios.ts @@ -25,7 +25,7 @@ const iOSTransformer: Transformer = { iOSTransformer.AUTOGENERATED_TAG as string ); if (generatedIndex >= 0) { - input = input.substr(0, generatedIndex); + input = input.slice(0, generatedIndex); } const output = input + iOSTransformer.AUTOGENERATED_TAG + EOL + newValues; diff --git a/packages/core/src/transformer/json.ts b/packages/core/src/transformer/json.ts index cdfac44c..03f8c6f9 100644 --- a/packages/core/src/transformer/json.ts +++ b/packages/core/src/transformer/json.ts @@ -18,7 +18,7 @@ const jsonTransformer: Transformer = { return ` "${key}" : "${normalizedValue}",`; }, async insert(_, newValues) { - newValues = newValues.substring(0, newValues.length - 1); + newValues = newValues.slice(0, -1); return `${EOL}{${EOL}${newValues}${EOL}}`; }, diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts index 9395eedc..e27ced26 100644 --- a/packages/core/src/utils.ts +++ b/packages/core/src/utils.ts @@ -1,8 +1,10 @@ -export const forceArray = (val: T | T[] | null) => { +export const forceArray = (val: T | T[] | null): T[] => { if (Array.isArray(val)) return val; if (!val) return []; return [val]; }; -export const isEqualCaseInsensitive = (string1: string, string2: string) => - string1.toLowerCase() === string2.toLowerCase(); +export const isEqualCaseInsensitive = ( + string1: string, + string2: string +): boolean => string1.toLowerCase() === string2.toLowerCase(); diff --git a/packages/core/src/writer.ts b/packages/core/src/writer.ts index 79d4baaa..7416819e 100644 --- a/packages/core/src/writer.ts +++ b/packages/core/src/writer.ts @@ -24,7 +24,7 @@ class FileWriter { lines: Line[], transformer: Transformer, encoding = "utf8" - ) { + ): Promise { let fileContent = ""; const { language, domain, outputDir } = fileInfo; const fileName = transformer.getFileName(language, domain); @@ -62,7 +62,7 @@ class FileWriter { lines: Line[], transformer: Transformer, meta: TransformLineMeta - ) { + ): Promise { let valueToInsert = ""; const plurals: { [pluralKey: string]: Line[] } = {}; @@ -88,6 +88,7 @@ class FileWriter { if (!plurals[line.key]) { plurals[line.key] = []; } + plurals[line.key].push(line); } else { valueToInsert += transformer.transformKeyValue(line.key, line.value); @@ -106,6 +107,7 @@ class FileWriter { if (typeof transformer.transformPluralsValues === "function") { return transformer.transformPluralsValues(key, plural); } + return ""; }) .join(EOL); diff --git a/packages/plugin-fallback/src/__tests__/index.test.ts b/packages/plugin-fallback/src/__tests__/index.test.ts index bfd5dd54..a2fed528 100644 --- a/packages/plugin-fallback/src/__tests__/index.test.ts +++ b/packages/plugin-fallback/src/__tests__/index.test.ts @@ -1,4 +1,4 @@ -import { GoogleSpreadsheetRow } from "google-spreadsheet"; +import type { GoogleSpreadsheetRow } from "google-spreadsheet"; import { Line } from "@lokse/core"; import fallbackPluginFactory from ".."; diff --git a/packages/plugin-fallback/src/index.ts b/packages/plugin-fallback/src/index.ts index 98e14609..d27d2578 100644 --- a/packages/plugin-fallback/src/index.ts +++ b/packages/plugin-fallback/src/index.ts @@ -16,7 +16,7 @@ export default function ( options: PluginOptions, { languages }: GeneralPluginMeta ): LoksePlugin { - const { defaultLanguage } = options; + const { defaultLanguage, logger } = options; if (!defaultLanguage) { throw new PluginError("Default language must be supplied"); @@ -39,14 +39,13 @@ export default function ( async readTranslation(line, meta) { if (line.key && !line.value) { const defaultLanguageKey = - Object.keys(meta.row).find(isDefaultLang) ?? NOT_FOUND_KEY; + Object.keys(meta.row).find((key) => isDefaultLang(key)) ?? + NOT_FOUND_KEY; const fallbackLanguageValue = meta.row[defaultLanguageKey] ?? ""; if (logMissingFallback && !fallbackLanguageValue) { - options.logger.warn( - `Fallback translation of key "${meta.key}" not found` - ); + logger.warn(`Fallback translation of key "${meta.key}" not found`); } line.setValue(fallbackLanguageValue); diff --git a/packages/plugin-non-breaking-spaces/package.json b/packages/plugin-non-breaking-spaces/package.json index d66a523b..f17c50e2 100644 --- a/packages/plugin-non-breaking-spaces/package.json +++ b/packages/plugin-non-breaking-spaces/package.json @@ -8,7 +8,8 @@ }, "bugs": "https://github.com/AckeeCZ/lokse/issues", "dependencies": { - "@lokse/core": "^2.1.3" + "@lokse/core": "^2.1.3", + "lodash": "^4.17.20" }, "engines": { "node": ">=8.0.0" diff --git a/packages/plugin-non-breaking-spaces/src/index.ts b/packages/plugin-non-breaking-spaces/src/index.ts index b502d7ec..e14690d0 100644 --- a/packages/plugin-non-breaking-spaces/src/index.ts +++ b/packages/plugin-non-breaking-spaces/src/index.ts @@ -11,8 +11,8 @@ export interface CustomPatterns { } const defaultPatterns: Patterns = { - cs: /(\s|^)(a|i|k|o|s|u|v|z)(\s+)/gim, - "cs-cz": /(\s|^)(a|i|k|o|s|u|v|z)(\s+)/gim, + cs: /(\s|^)([aikosuvz])(\s+)/gim, + "cs-cz": /(\s|^)([aikosuvz])(\s+)/gim, }; export interface PluginOptions extends GeneralPluginOptions { diff --git a/packages/plugin-non-breaking-spaces/src/utils.ts b/packages/plugin-non-breaking-spaces/src/utils.ts index 9372b84a..d99f742d 100644 --- a/packages/plugin-non-breaking-spaces/src/utils.ts +++ b/packages/plugin-non-breaking-spaces/src/utils.ts @@ -2,11 +2,14 @@ import { mapKeys, mapValues } from "lodash"; import { CustomPatterns } from "."; -export const lowerCaseKeys = (patterns: CustomPatterns) => - mapKeys(patterns, (_, key) => key.toLowerCase()); +export const lowerCaseKeys = ( + patterns: CustomPatterns +): Record => mapKeys(patterns, (_, key) => key.toLowerCase()); const convertStringToRegex = (string: string) => new RegExp(`(\\s|^)${string}(\\s+)`, "gim"); -export const regexifyValues = (patterns: CustomPatterns) => +export const regexifyValues = ( + patterns: CustomPatterns +): Record => mapValues(patterns, (value) => convertStringToRegex(value));