Skip to content

Commit

Permalink
Improved eslint setup.
Browse files Browse the repository at this point in the history
- Added Unicode block support to `Character` (for the tests mostly).
- Added more eslint rules, specifically to have correct jsdoc.
  • Loading branch information
mike-lischke committed Oct 15, 2024
1 parent 928e1f6 commit 047d095
Show file tree
Hide file tree
Showing 8 changed files with 2,561 additions and 797 deletions.
91 changes: 72 additions & 19 deletions build/generate-unicode-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
* with that data. The file is then used by the runtime to support Unicode properties and categories.
*/

import * as fs from "fs/promises";
import { createWriteStream } from "fs";
import { readdir, readFile, stat } from "fs/promises";
import { join, dirname } from "path";

import { IntervalSet } from "antlr4ng";
Expand Down Expand Up @@ -120,11 +121,12 @@ const intervalSetFromImport = async (file: string): Promise<IntervalSet> => {

// Generate the Unicode data file.
const targetPath = join(dirname(import.meta.url), "../src/generated/UnicodeData.ts").substring("file:".length);
await fs.writeFile(targetPath, "// Data generated by build/generate-unicode-data.ts. Do not edit.\n\n");
await fs.appendFile(targetPath, `import { IntervalSet } from "antlr4ng";\n\n`);
await fs.appendFile(targetPath, `/** A mapping from a Unicode property type to a set of code points. */\n`);
await fs.appendFile(targetPath, `export const propertyCodePointRanges = new Map<string, IntervalSet>();\n\n`);
await fs.appendFile(targetPath, `let set: IntervalSet;\n\n`);
const writer = createWriteStream(targetPath);

writer.write("// Data generated by build/generate-unicode-data.ts. Do not edit.\n\n");
writer.write(`import { IntervalSet } from "antlr4ng";\n\n`);
writer.write(`/** A mapping from a Unicode property type to a set of code points. */\n`);
writer.write(`export const propertyCodePointRanges = new Map<string, IntervalSet>();\n\n`);

const generateMap = async (basePath: string): Promise<void> => {
console.log(`Generating map for ${basePath}...`);
Expand All @@ -134,49 +136,92 @@ const generateMap = async (basePath: string): Promise<void> => {

// Enumerate all folders in the base path.
const folderURL = join(sourceURL, basePath);
const elements = await fs.readdir(folderURL);
const elements = await readdir(folderURL);
for (const element of elements) {
// Is the element a folder?
const target = join(folderURL, element);
const s = await fs.stat(target);
const s = await stat(target);
if (!s.isDirectory()) {
continue;
}

await fs.appendFile(targetPath, `set = new IntervalSet();\n`);
writer.write(`set = new IntervalSet();\n`);
const set = await intervalSetFromImport(`${target}/ranges.js`);
let counter = 0;
for (const range of set) {
await fs.appendFile(targetPath, `set.addRange(0x${numberToHex(range.start)}, ` +
writer.write(`set.addRange(0x${numberToHex(range.start)}, ` +
`0x${numberToHex(range.stop)}); `);
++counter;
if (counter === 5) {
await fs.appendFile(targetPath, "\n");
writer.write("\n");
counter = 0;
}
}

if (counter > 0) {
await fs.appendFile(targetPath, "\n");
writer.write("\n");
}

const elementName = element.toLocaleLowerCase();
if (abbr) {
// Enumerated values.
const list = aliases.get(abbr);
const abbreviated = list?.get(elementName);
await fs.appendFile(targetPath,
`propertyCodePointRanges.set("${abbr}=${abbreviated ?? elementName}", set);\n\n`);
writer.write(`propertyCodePointRanges.set("${abbr}=${abbreviated ?? elementName}", set);\n\n`);
} else {
// Catalog properties, enumerated property names, general categories and binary properties.
const abbreviated = mainList?.get(elementName);
await fs.appendFile(targetPath,
`propertyCodePointRanges.set("${abbreviated ?? elementName}", set);\n\n`);
writer.write(`propertyCodePointRanges.set("${abbreviated ?? elementName}", set);\n\n`);
}
}
};

let content = await fs.readFile(join(dirname(import.meta.url).substring("file:".length), "PropertyAliases.txt"),
const generateBlocksMap = async (): Promise<void> => {
console.log(`Collecting Unicode blocks data...`);

const folderURL = join(sourceURL, "Block");
const elements = await readdir(folderURL);

const ranges = new Map<string, string>();
const nameMap = new Map<string, string>();

writer.write(`export class UnicodeBlockConstants {\n`);
for (let i = 0; i < elements.length; ++i) {
const element = elements[i];

const target = join(folderURL, element);
const s = await stat(target);
if (!s.isDirectory() || element === "undefined") {
continue;
}

writer.write(` public static ${element.toUpperCase()} = ${i};\n`);

// Block ranges always contain only one entry.
const content = await import(`${target}/ranges.js`) as IDataFileContent;

ranges.set(`UnicodeBlockConstants.${element.toUpperCase()}`,
`[0x${numberToHex(content.default[0].begin)}, ` +
`0x${numberToHex(content.default[0].end)}]`);
nameMap.set(element, `UnicodeBlockConstants.${element.toUpperCase()}`);
}

// Write collected maps.
writer.write(`\n public static readonly ranges = new Map<number, [number, number]>([\n`);
for (const [key, value] of ranges) {
writer.write(` [${key}, ${value}], \n`);
}
writer.write(` ]); \n`);

writer.write(`\n public static readonly names = new Map<string, number>([\n`);
for (const [key, value] of nameMap) {
writer.write(` ["${key.replace(/_/g, "").toLowerCase()}", ${value}], \n`);
}

writer.write(" ]);\n\n}\n\n");
};

let content = await readFile(join(dirname(import.meta.url).substring("file:".length), "PropertyAliases.txt"),
"utf8");
const lines = content.split("\n");
const list = aliases.get("");
Expand All @@ -193,6 +238,10 @@ for (const line of lines) {
}
}

await generateBlocksMap();

writer.write(`let set: IntervalSet; \n\n`);

await generateMap("Bidi_Class");
await generateMap("Binary_Property");
await generateMap("Block");
Expand All @@ -203,5 +252,9 @@ await generateMap("Script_Extensions");
await generateMap("Word_Break");

// Finally add the aliases. These were taken from the Java Unicode data generator (which uses ICU for that).
content = await fs.readFile(join(dirname(import.meta.url).substring("file:".length), "aliases.json"), "utf8");
await fs.appendFile(targetPath, `export const propertyAliases = new Map<string, string>(${content});\n`);
content = await readFile(join(dirname(import.meta.url).substring("file:".length), "aliases.json"), "utf8");
writer.write(`export const propertyAliases = new Map<string, string>(${content}); \n`);

writer.close();

console.log("\nUnicode data generation completed.\n");
44 changes: 31 additions & 13 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,34 @@
import eslint from "@eslint/js";
import tslint from "typescript-eslint";
import stylistic from "@stylistic/eslint-plugin";
import typescriptEslintParser from "@typescript-eslint/parser";
import jsdoc from "eslint-plugin-jsdoc";
import preferArrow from "eslint-plugin-prefer-arrow";
import importPlugin from "eslint-plugin-import";

export default tslint.config(
eslint.configs.recommended,
...tslint.configs.strictTypeChecked,
...tslint.configs.stylisticTypeChecked,
jsdoc.configs["flat/recommended"],
{
plugins: {
"@stylistic": stylistic
"@stylistic": stylistic,
"jsdoc": jsdoc,
"prefer-arrow": preferArrow,
"import": importPlugin,
},
languageOptions: {
parser: typescriptEslintParser,
parser: tslint.parser,
parserOptions: {
projectService: {
allowDefaultProject: ["*.js", "*.mjs", "*.ts"],
defaultProject: "tsconfig.json",
allowDefaultProject: ["*.mjs", "jest.config.ts"],
project: "tsconfig.json",
},
tsconfigRootDir: import.meta.dirname,
project: [
"./tsconfig.json",
"./tests/tsconfig.json"
],
allowDefaultProject: [
"./tests"
]
sourceType: "module",
},
},
ignores: ["src/generated/*"],
rules: {
"no-fallthrough": [
"warn",
Expand Down Expand Up @@ -161,6 +162,23 @@ export default tslint.config(
"@typescript-eslint/prefer-return-this-type": "off",
"@typescript-eslint/no-invalid-void-type": "off",
"@typescript-eslint/unified-signatures": "off",
"jsdoc/check-alignment": "error",
"jsdoc/check-indentation": "off",
"jsdoc/require-param-type": "off",
"jsdoc/require-returns-type": "off",
"jsdoc/tag-lines": [
// Have to switch this off, as it is not good enough to be used.
"off"
],
"prefer-arrow/prefer-arrow-functions": [
"warn",
{
"disallowPrototype": true,
"singleReturnOnly": false,
"classPropertiesAllowed": false
}
],

},
},
}
);
Loading

0 comments on commit 047d095

Please sign in to comment.