Skip to content

Commit

Permalink
More type and type-related fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
wadimw committed Feb 14, 2025
1 parent 5443b29 commit cef2dd3
Show file tree
Hide file tree
Showing 13 changed files with 101 additions and 63 deletions.
2 changes: 1 addition & 1 deletion packages/ilib-lint-common/src/FileStats.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class FileStats {
* the result.
*
* @param {FileStats} stats the other instance to add to the current one
* @returns {FileStats] the current instance
* @returns {FileStats} the current instance
*/
addStats(stats) {
if (!stats || typeof(stats) !== 'object' || !(stats instanceof FileStats)) return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
*/

import FileStats from "./FileStats.js";
import SourceFile from "./SourceFile.js";

/**
* @class Representation of parser results
Expand Down
4 changes: 2 additions & 2 deletions packages/ilib-lint-common/src/Serializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
import NotImplementedError from "./NotImplementedError.js";
import PipelineElement from "./PipelineElement.js";

/** @ignore @typedef {import("./IntermediateRepresentation.js").default} IntermediateRepresentation */
/** @ignore @typedef {import("./SourceFile.js").default} SourceFile */
import IntermediateRepresentation from "./IntermediateRepresentation.js";
import SourceFile from "./SourceFile.js";

/**
* @class common SPI for serializer plugins
Expand Down
7 changes: 3 additions & 4 deletions packages/ilib-lint-common/src/Transformer.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@

import NotImplementedError from "./NotImplementedError.js";
import PipelineElement from "./PipelineElement.js";

/** @ignore @typedef {import("./IntermediateRepresentation.js").default} IntermediateRepresentation */
/** @ignore @typedef {import("./Result.js").default} Result */
import IntermediateRepresentation from "./IntermediateRepresentation.js";
import Result from "./Result.js";

/**
* @class common SPI for transformer plugins
Expand Down Expand Up @@ -67,6 +66,6 @@ class Transformer extends PipelineElement {
transform(representation, results) {
throw new NotImplementedError();
}
};
}

export default Transformer;
17 changes: 12 additions & 5 deletions packages/ilib-lint/src/DirItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
* limitations under the License.
*/

import log4js from 'log4js';
import log4js from "log4js";

import { IntermediateRepresentation, Result } from "ilib-lint-common";
import Locale from "ilib-locale";

const logger = log4js.getLogger("ilib-lint.DirItem");

Expand Down Expand Up @@ -66,16 +69,20 @@ class DirItem {
* representations of this file
* @abstract
*/
parse() {}
parse() {
throw new Error("Method not implemented.");
}

/**
* Check the directory item and return a list of issues found in it.
*
* @param {Array.<Locale>} locales a set of locales to apply
* @param {Array.<Locale|string>} locales a set of locales to applys
* @returns {Array.<Result>} a list of natch results
* @abstract
*/
findIssues(locales) {}
};
findIssues(locales) {
throw new Error("Method not implemented.");
}
}

export default DirItem;
75 changes: 44 additions & 31 deletions packages/ilib-lint/src/FileType.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import log4js from 'log4js';

import RuleSet from './RuleSet.js';
import Project from './Project.js';
import { Parser, Rule, Serializer, Transformer } from 'ilib-lint-common';

const logger = log4js.getLogger("ilib-lint.FileType");

Expand Down Expand Up @@ -63,10 +65,10 @@ class FileType {
parsers = undefined;

/**
* The array of classes of parsers to use with this file type.
* @type {Array.<Class>|undefined}
* The array of parsers to use with this file type.
* @type {Array.<Parser>|undefined}
*/
parserClasses = undefined;
parserInstances = undefined;

/**
* The array of names of transformers to use with this file type.
Expand Down Expand Up @@ -118,6 +120,7 @@ class FileType {
* of this file type as documented above
* @param {String} options.name the name or glob spec for this file type
* @param {Project} options.project the Project that this file type is a part of
* @param {string} [options.type] The intermediate representation type of this file type
* @param {Array.<String>} [options.locales] list of locales to use with this file type
* @param {String} [options.template] the path name template for this file type
* which shows how to extract the locale from the path
Expand All @@ -138,7 +141,7 @@ class FileType {
* @param {Array.<String>} [options.transformers] an array of transformer names
* to apply to files of this type after the rules have been applied. Every transformer
* must operate on the same type of intermediate representation as the parser.
* @param {String|Object} [options.serializer] the name of the serializer to use if
* @param {String|Serializer} [options.serializer] the name of the serializer to use if
* the file has been modified by a transformer or a fixer. The serializer must
* operate on the same type of intermediate representation as the parser.
* @constructor
Expand All @@ -150,21 +153,27 @@ class FileType {
if (!options || !options.name || !options.project) {
throw "Missing required options to the FileType constructor";
}
["name", "project", "locales", "ruleset", "template", "parsers", "transformers", "serializer"].forEach(prop => {
if (typeof(options[prop]) !== 'undefined') {
this[prop] = options[prop];
}
});

this.name = options.name;
this.project = options.project;
this.locales = options.locales;
this.template = options.template;
this.parsers = options.parsers;
this.ruleset = options.ruleset;
this.transformers = options.transformers;

// temp variable because the type may be inferred from other provided options
let maybeType = options.type;

if (this.parsers) {
const parserMgr = this.project.getParserManager();
this.parserClasses = this.parsers.map(parserName => {
this.parserInstances = this.parsers.map(parserName => {
const parser = parserMgr.getByName(parserName);
if (!parser) {
throw `Could not find parser ${parserName} named in the configuration for filetype ${this.name}`;
}
if (!this.type) {
this.type = parserMgr.getType(parserName);
if (!maybeType) {
maybeType = parserMgr.getType(parserName);
}
return parser;
});
Expand Down Expand Up @@ -195,35 +204,36 @@ class FileType {
throw `Could not find transformer ${transformerName} named in the configuration for filetype ${this.name}`;
}
const transformerType = transformer.getType();
if (!this.type) {
this.type = transformerType;
} else if (transformerType !== this.type) {
throw new Error(`The transformer ${transformerName} processes representations of type ${transformerType}, but the filetype ${this.name} handles representations of type ${this.type}. The two types must match.`);
if (!maybeType) {
maybeType = transformerType;
} else if (transformerType !== maybeType) {
throw new Error(`The transformer ${transformerName} processes representations of type ${transformerType}, but the filetype ${this.name} handles representations of type ${maybeType}. The two types must match.`);
}
return transformer;
});
}

if (this.serializer) {
if (options.serializer) {
// if it is a string, then that string is the name of the serializer. If it is an object,
// then it the name and the settings to pass to the the serializer constructor.
const name = typeof(this.serializer) === 'string' ? this.serializer : this.serializer.name;
const serializerName = typeof(options.serializer) === 'string' ? options.serializer : options.serializer.name;
const serializerMgr = this.project.getSerializerManager();
this.serializerInst = serializerMgr.get(name, this.serializer);
this.serializerInst = serializerMgr.get(serializerName, this.serializer);
if (!this.serializerInst) {
throw new Error(`Could not find or instantiate serializer ${this.serializer} named in the configuration for filetype ${this.name}`);
throw new Error(`Could not find or instantiate serializer ${serializerName} named in the configuration for filetype ${this.name}`);
}
const serializerType = this.serializerInst.getType();
if (!this.type) {
this.type = serializerType;
} else if (serializerType !== this.type) {
throw new Error(`The serializer ${name} processes representations of type ${serializerType}, but the filetype ${this.name} handles representations of type ${this.type}. The two types must match.`);
if (!maybeType) {
maybeType = serializerType;
} else if (serializerType !== maybeType) {
throw new Error(`The serializer ${serializerName} processes representations of type ${serializerType}, but the filetype ${this.name} handles representations of type ${maybeType}. The two types must match.`);
}
}

if (!this.type) {
this.type = "string";
if (!maybeType) {
maybeType = "string";
}
this.type = maybeType;
}

getName() {
Expand Down Expand Up @@ -253,11 +263,11 @@ class FileType {
* that can parse files with the given file name extension. If there
* are none available, this method returned undefined;
* @param {String} extension file name extension of the file being parsed
* @returns {Array.<Class>} an array of parser classes to use with
* @returns {Array.<Parser>} an array of parser classes to use withs
* files of this type.
*/
getParserClasses(extension) {
if (this.parserClasses) return this.parserClasses;
if (this.parserInstances) return this.parserInstances;
const pm = this.project.getParserManager();
return pm.get(extension);
}
Expand All @@ -268,7 +278,7 @@ class FileType {
* @returns {Array.<String>} a list of rule set names
*/
getRuleSetNames() {
return this.ruleset;
return this.ruleset ?? [];
}

/**
Expand All @@ -291,17 +301,20 @@ class FileType {
return;
}
for (let ruleName in definitions) {
let maybeRule;
if (typeof(definitions[ruleName]) === 'boolean') {
if (definitions[ruleName]) {
set.addRule(ruleMgr.get(ruleName));
maybeRule = ruleMgr.get(ruleName);

} else {
// else explicitly turn the rule off
set.removeRule(ruleName);
}
} else {
// only pass in the optional parameter if it is not boolean
set.addRule(ruleMgr.get(ruleName, definitions[ruleName]));
maybeRule = ruleMgr.get(ruleName, definitions[ruleName]);
}
if (maybeRule) set.addRule(maybeRule);
}
});
// the RuleSet takes care of making sure there are no dups, so now we
Expand Down
10 changes: 7 additions & 3 deletions packages/ilib-lint/src/LintableFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import path from "node:path";
import log4js from "log4js";
import { getLocaleFromPath } from "ilib-tools-common";
import { Fix, IntermediateRepresentation, Parser, Result, SourceFile, FileStats } from "ilib-lint-common";
import { Fix, IntermediateRepresentation, Parser, Result, SourceFile, FileStats, Serializer } from "ilib-lint-common";
import DirItem from "./DirItem.js";
import Project from "./Project.js";
import FileType from "./FileType.js";
Expand Down Expand Up @@ -72,6 +72,7 @@ class LintableFile extends DirItem {
* @param {FileType} options.filetype file type of this source file
* @param {String} options.filePath path to the file
* @param {object} [options.settings] additional settings from the ilib-lint config that apply to this file
* @param {string} [options.locale] the locale of this file
* @param {Project} project the project where this file is located
*/
constructor(filePath, options, project) {
Expand Down Expand Up @@ -130,7 +131,7 @@ class LintableFile extends DirItem {
}
});
if (!irs || irs.length === 0) {
throw new Error(`All available parsers failed to parse file ${file.sourceFile.getPath()}. Try configuring another parser or excluding this file from the lint project.`);
throw new Error(`All available parsers failed to parse file ${this.sourceFile.getPath()}. Try configuring another parser or excluding this file from the lint project.`);
}
return irs;
}
Expand Down Expand Up @@ -245,7 +246,10 @@ class LintableFile extends DirItem {
if (this.parsers.length === 1) {
// if this is the only parser for this file, throw an exception right away so the user
// can see what the specific parse error was from the parser
throw new Error(`Could not parse file ${this.sourceFile.getPath()}. Try configuring another parser or excluding this file from the lint project.`, {
throw new Error(`Could not parse file ${this.sourceFile.getPath()}. Try configuring another parser or excluding this file from the lint project.`,
// @ts-ignore -- options.cause is available since Node 16,
// but in older versions the extra arg it's simply ignored so it can be kept here
{
cause: e
});
}
Expand Down
23 changes: 13 additions & 10 deletions packages/ilib-lint/src/ParserManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,31 @@ import log4js from 'log4js';

import { Parser } from 'ilib-lint-common';

/** @ignore @typedef {typeof import("ilib-lint-common").Parser} ParserClass */

const logger = log4js.getLogger("ilib-lint.ParserManager");

function getSuperClassName(obj) {
return Object.getPrototypeOf(Object.getPrototypeOf(obj)).constructor.name;
}

/**
* @typedef {Object} ParserInfo Information about a parser.
*
* @property {string} description a description of the parser
* @property {string} type the type of parser
* @property {Array.<string>} extensions an array of file name extensions that this parser can handle
*/

/**
* @class Manages a collection of parsers that this instance of ilib-lint
* knows about.
*/
class ParserManager {
/**
* Information about the parsers that this instance of ilib-lint knows about.
* Each entry in the object is a parser name and the value is an object
* with the properties:
* <ul>
* <li>description - a description of the parser</li>
* <li>type - the type of parser</li>
* <li>extensions - an array of file name extensions that this parser can handle</li>
* </ul>
*
* @type {Object}
* Each entry in the object is a parser name and the value is a {@link ParserInfo} object
* @type {Object.<string, ParserInfo>}
* @private
*/
parserInfo = {};
Expand Down Expand Up @@ -85,7 +88,7 @@ class ParserManager {
* Add a list of parsers to this factory so that other code
* can find them.
*
* @param {Array.<Parser>} parsers the list of parsers to add
* @param {Array.<ParserClass>} parsers the list of parsers to add
*/
add(parsers) {
if (!parsers || !Array.isArray(parsers)) return;
Expand Down
13 changes: 11 additions & 2 deletions packages/ilib-lint/src/Project.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,14 @@ import path from 'node:path';
import log4js from 'log4js';
import mm from 'micromatch';

import { FileStats, SourceFile } from 'ilib-lint-common';
import { FileStats, SourceFile, Result } from 'ilib-lint-common';

import PluginManager from "./PluginManager.js";
import ParserManager from "./ParserManager.js";
import RuleManager from "./RuleManager.js";
import FixerManager from "./FixerManager.js";
import TransformerManager from "./TransformerManager.js";
import SerializerManager from "./SerializerManager.js";

import LintableFile from './LintableFile.js';
import DirItem from './DirItem.js';
Expand Down Expand Up @@ -117,6 +124,8 @@ class Project extends DirItem {
"xliff": new FileType({project: this, ...xliffFileTypeDefinition}),
"unknown": new FileType({project: this, ...unknownFileTypeDefinition})
};

this.fileStats = new FileStats();
}

/**
Expand Down Expand Up @@ -461,6 +470,7 @@ class Project extends DirItem {
* there is no such file type
*/
getFileType(name) {
throw new Error("Method not implemented.");
}

/**
Expand Down Expand Up @@ -533,7 +543,6 @@ class Project extends DirItem {
* @returns {Array.<Result>} a list of results
*/
findIssues(locales) {
this.fileStats = new FileStats();
return this.files.flatMap(file => {
//logger.debug(`Examining ${file.filePath}`);
if (!this.options.opt.quiet && this.options.opt.progressInfo) {
Expand Down
Loading

0 comments on commit cef2dd3

Please sign in to comment.