Skip to content

Commit

Permalink
chore: clean up codebase
Browse files Browse the repository at this point in the history
  • Loading branch information
blurrah committed Jul 1, 2024
1 parent 12f90af commit a05d1fa
Show file tree
Hide file tree
Showing 13 changed files with 110 additions and 96 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
**/*.js
examples/
.eslintrc.cjs
3 changes: 3 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Examples

Few example use cases to try out changes, use tests if you want to properly test things.
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"author": "Lab Digital",
"type": "module",
"bin": {
"intl-extractor": "./dist/cli.cjs"
"intl-extractor": "./dist/bin/cli.js"
},
"scripts": {
"build": "tsup",
Expand All @@ -20,6 +20,9 @@
"glob": "^10.4.2",
"yargs": "^17.7.2"
},
"peerDependencies": {
"typescript": ">=5.5"
},
"devDependencies": {
"@changesets/cli": "^2.27.6",
"@types/node": "^20.14.9",
Expand Down
2 changes: 1 addition & 1 deletion src/cli.ts → src/bin/cli.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { processFiles } from "./main";
import { processFiles } from "../main";

// Setup yargs to use the command modules from the commands directory

Expand Down
8 changes: 4 additions & 4 deletions src/cache.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { describe, expect, test } from "vitest";
import { updateCache } from "./main";
import { updateLabelCache } from "./cache";

describe("cache", () => {
test("writes output to empty cache", () => {
const cache = {};

updateCache({
updateLabelCache({
cache,
data: {
Test: new Set(["hello"]),
Expand Down Expand Up @@ -34,7 +34,7 @@ describe("cache", () => {
},
};

updateCache({
updateLabelCache({
cache,
data: {
Test: new Set(["test"]),
Expand All @@ -58,7 +58,7 @@ describe("cache", () => {
test("updates labels with existing source", () => {
const cache = {};

updateCache({
updateLabelCache({
cache,
data: {
Test: new Set(["hello"]),
Expand Down
61 changes: 61 additions & 0 deletions src/cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { type LabelData } from "./types";

/**
* Update existing label cache based on given data and source labels
*/
export function updateLabelCache({
cache,
source,
data,
}: {
cache: LabelData;
source: LabelData;
data: Record<string, Set<string>>;
}) {
for (const [key, values] of Object.entries(data)) {
// Next-intl uses dot notation for nested objects
const keys = key.split(".");
let currentCache = cache;

// Set up the namespace in the cache
for (let i = 0; i < keys.length; i++) {
const currentKey = keys[i];
currentCache[currentKey] = currentCache[currentKey] || {};
currentCache = currentCache[currentKey] as LabelData;
}

// Add values for each label, try the existing source first
// or use the namespace with name as a value
for (const value of values) {
currentCache[value] =
getLabelFromData(source, [...keys, value]) || `${key}.${value}`;
}
}
}

/**
* Traverse through label data and find existing label or return undefined
* @param path Array of keys to traverse through
* @param source Label data object to get label from
* @returns String value if available or undefined if not
*/
function getLabelFromData(
source: LabelData,
path: Array<string>
): string | undefined {
let current: LabelData | string = source;
for (const key of path) {
if (
typeof current !== "object" ||
current[key] === undefined ||
!(key in current)
) {
return undefined;
}

current = current[key];
}

// Only return label if it's actually a string
return typeof current === "string" ? current : undefined;
}
2 changes: 1 addition & 1 deletion src/parser.test.ts → src/extract.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, test } from "vitest";
import { extractLabels } from "./parser";
import { extractLabels } from "./extract";

describe("Test parseSource", () => {
test("should parse source", async () => {
Expand Down
File renamed without changes.
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { extractLabels, extractLabelsFromFile } from "./extract";
74 changes: 4 additions & 70 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
import * as glob from "glob";
import * as fs from "node:fs";
import { extractLabelsFromFile } from "./parser";

/**
* Recursive type for labels which can be nested objects containing strings
*/
type LabelData = {
[key: string]: string | LabelData;
};
import { updateLabelCache } from "./cache";
import { extractLabelsFromFile } from "./extract";
import { type LabelData } from "./types";

/**
* Main function that collects labels, source file and writes it to the output
* @param rootPath Root path of typescript files to check
* @param output JSON file to use for output labels
*
*/
export async function processFiles(
directories: Array<string>,
Expand Down Expand Up @@ -52,70 +46,10 @@ export async function processFiles(
if (Object.keys(data).length > 0) {
console.info(`Updating labels for ${file}`);
// This might not be performant as we do existign source look ups for every added file
updateCache({ cache, data, source });
updateLabelCache({ cache, data, source });
}
}

// Write the new output
fs.promises.writeFile(output, JSON.stringify(cache, null, "\t") + "\n");
}

/**
* Update existing cache based on given data and source labels
*/
export function updateCache({
cache,
source,
data,
}: {
cache: LabelData;
source: LabelData;
data: Record<string, Set<string>>;
}) {
for (const [key, values] of Object.entries(data)) {
// Next-intl uses dot notation for nested objects
const keys = key.split(".");
let currentCache = cache;

// Set up the namespace in the cache
for (let i = 0; i < keys.length; i++) {
const currentKey = keys[i];
currentCache[currentKey] = currentCache[currentKey] || {};
currentCache = currentCache[currentKey] as LabelData;
}

// Add values for each label, try the existing source first
// or use the namespace with name as a value
for (const value of values) {
currentCache[value] =
getLabelFromData(source, [...keys, value]) || `${key}.${value}`;
}
}
}

/**
* Traverse through label data and find existing label or return undefined
* @param path Array of keys to traverse through
* @param source Label data object to get label from
* @returns String value if available or undefined if not
*/
function getLabelFromData(
source: LabelData,
path: Array<string>
): string | undefined {
let current: LabelData | string = source;
for (const key of path) {
if (
typeof current !== "object" ||
current[key] === undefined ||
!(key in current)
) {
return undefined;
}

current = current[key];
}

// Only return label if it's actually a string
return typeof current === "string" ? current : undefined;
}
6 changes: 6 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* Recursive type for labels which can be nested objects containing strings
*/
export type LabelData = {
[key: string]: string | LabelData;
};
35 changes: 20 additions & 15 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Backend",
"compilerOptions": {
// Enable latest features
"lib": ["ESNext", "DOM"],
"target": "ESNext",
"module": "ESNext",
"moduleDetection": "force",
"jsx": "react-jsx",
"allowJs": true,
"composite": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"isolatedModules": true,
"module": "ES2022",

// Bundler mode
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"noEmit": true,
"outDir": "dist",
"preserveWatchOutput": true,
"resolveJsonModule": true,
"sourceMap": true,
"skipLibCheck": true,

// Best practices
"strict": true,
"strictPropertyInitialization": false,
"target": "ES2022"
"skipLibCheck": true,
"noFallthroughCasesInSwitch": true,

// Some stricter flags (disabled by default)
"noUnusedLocals": true,
"noUnusedParameters": true,
"noPropertyAccessFromIndexSignature": true
},
"exclude": ["node_modules/*", "**/node_modules/*"]
"include": ["./src/**/*"]
}
8 changes: 4 additions & 4 deletions tsup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { defineConfig } from "tsup";

export default defineConfig([
{
entry: ["src/cli.ts"],
entry: ["src/bin/cli.ts", "src/index.ts"],
clean: true,
splitting: false,
dts: false,
sourcemap: false,
format: ["cjs"],
dts: true,
sourcemap: true,
format: ["esm"],
outDir: "dist",
shims: true,
},
Expand Down

0 comments on commit a05d1fa

Please sign in to comment.