Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
9 changes: 9 additions & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
approvedGitRepositories:
- '**'

compressionLevel: mixed

enableGlobalCache: false

enableScripts: true

nodeLinker: node-modules

npmMinimalAgeGate: 0
35 changes: 21 additions & 14 deletions lib/parse/ClassLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,16 +95,14 @@ export class ClassLoader {
fileName: string,
): GenericallyTyped<string>[] {
const interfaceNames: GenericallyTyped<string>[] = [];
if (declaration.implements) {
for (const implement of declaration.implements) {
if (implement.expression.type !== AST_NODE_TYPES.Identifier) {
throw new Error(`Could not interpret the implements type on a class in ${fileName} on line ${implement.expression.loc.start.line} column ${implement.expression.loc.start.column}`);
}
interfaceNames.push({
value: implement.expression.name,
genericTypeInstantiations: implement.typeArguments,
});
for (const implement of declaration.implements) {
if (implement.expression.type !== AST_NODE_TYPES.Identifier) {
throw new Error(`Could not interpret the implements type on a class in ${fileName} on line ${implement.expression.loc.start.line} column ${implement.expression.loc.start.column}`);
}
interfaceNames.push({
value: implement.expression.name,
genericTypeInstantiations: implement.typeArguments,
});
}
return interfaceNames;
}
Expand Down Expand Up @@ -382,7 +380,7 @@ export class ClassLoader {
},
typeAnnotation: {
type: AST_NODE_TYPES.TSLiteralType,
literal: enumMember.initializer,
literal: <TSESTree.TSLiteralType['literal']> enumMember.initializer,
loc: <any> undefined,
range: <any> undefined,
parent: <any> undefined,
Expand Down Expand Up @@ -551,6 +549,15 @@ export class ClassLoader {
};
}

/**
* Get the name of an import or export specifier,
* which can either be an identifier or a string literal, such as in `export { A as "b" }`.
* @param name A specifier name node.
*/
public getSpecifierName(name: TSESTree.Identifier | TSESTree.StringLiteral): string {
return name.type === AST_NODE_TYPES.Identifier ? name.name : name.value;
}

/**
* Get all class elements in a file.
* @param packageName Package name we are importing from.
Expand Down Expand Up @@ -612,8 +619,8 @@ export class ClassLoader {
for (const specifier of statement.specifiers) {
const entry = this.importTargetToAbsolutePath(packageName, fileName, statement.source.value);
if (entry) {
exportedImportedElements[specifier.exported.name] = {
localName: specifier.local.name,
exportedImportedElements[this.getSpecifierName(specifier.exported)] = {
localName: this.getSpecifierName(specifier.local),
qualifiedPath: undefined,
...entry,
};
Expand All @@ -622,7 +629,7 @@ export class ClassLoader {
} else {
// Form: `export { A as B }`
for (const specifier of statement.specifiers) {
exportedUnknowns[specifier.exported.name] = specifier.local.name;
exportedUnknowns[this.getSpecifierName(specifier.exported)] = this.getSpecifierName(specifier.local);
}
}
} else if (statement.type === AST_NODE_TYPES.ExportAllDeclaration) {
Expand Down Expand Up @@ -673,7 +680,7 @@ export class ClassLoader {
if (specifier.type === AST_NODE_TYPES.ImportSpecifier) {
// Form: `import {A} from './lib/A'`
importedElements[specifier.local.name] = {
localName: specifier.imported.name,
localName: this.getSpecifierName(specifier.imported),
qualifiedPath: undefined,
...entry,
};
Expand Down
1 change: 0 additions & 1 deletion lib/parse/ParameterLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,6 @@ export class ParameterLoader {
return { type: 'raw', value: 'string' };
case AST_NODE_TYPES.TSLiteralType:
if (typeNode.literal.type !== AST_NODE_TYPES.UnaryExpression &&
typeNode.literal.type !== AST_NODE_TYPES.UpdateExpression &&
'value' in typeNode.literal &&
(typeof typeNode.literal.value === 'number' ||
typeof typeNode.literal.value === 'string' ||
Expand Down
2 changes: 1 addition & 1 deletion lib/parse/ParameterResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ export class ParameterResolver {
classOrInterface,
{
type: AST_NODE_TYPES.TSLiteralType,
literal: enumMember.initializer,
literal: <TSESTree.TSLiteralType['literal']> enumMember.initializer,
loc: <any> undefined,
range: <any> undefined,
parent: <any> undefined,
Expand Down
15 changes: 9 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "componentsjs-generator",
"version": "4.3.0",
"packageManager": "yarn@4.7.0",
"packageManager": "yarn@4.18.0",
"description": "Automatically generate component files from TypeScript classes for the Components.js dependency injection framework",
"author": "Ruben Taelman <rubensworks@gmail.com>",
"license": "MIT",
Expand Down Expand Up @@ -38,31 +38,34 @@
},
"dependencies": {
"@types/semver": "^7.0.0",
"@typescript-eslint/typescript-estree": "^7.1.0",
"@typescript-eslint/typescript-estree": "^8.0.0",
"comment-parser": "^1.4.0",
"componentsjs": "^6.3.0",
"componentsjs": "^6.4.0",
"jsonld-context-parser": "^3.0.0",
"lru-cache": "^11.0.0",
"minimist": "^1.2.0",
"rdf-object": "^2.0.0",
"rdf-object": "^3.0.0",
"semver": "^7.0.0"
},
"devDependencies": {
"@rubensworks/eslint-config": "^3.0.0",
"@types/fs-extra": "^11.0.0",
"@types/jest": "^29.0.0",
"@types/jest": "^30.0.0",
"@types/minimist": "^1.2.0",
"@types/node": "^24.0.0",
"@types/rimraf": "^4.0.0",
"eslint": "^8.0.0",
"fs-extra": "^11.0.0",
"husky": "^9.0.0",
"jest": "^29.0.0",
"jest": "^30.0.0",
"jest-extended": "^7.0.0",
"manual-git-changelog": "^1.0.0",
"rimraf": "^6.0.1",
"ts-jest": "^29.0.0",
"ts-node": "^10.0.0",
"typescript": "^5.0.0"
},
"resolutions": {
"@comunica/types/lru-cache": "^11.0.0"
}
}
47 changes: 40 additions & 7 deletions test/parse/ClassLoader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,21 +87,21 @@ describe('ClassLoader', () => {
]);
});

it('should ignore on an interface that is extended anonymously', async() => {
it('should ignore on an interface that is extended via a qualified name', async() => {
expect(loader.getSuperInterfaceNames(<any>(resolutionContext
.parseTypescriptContents('interface A extends {} {}')).body[0], 'file'))
.parseTypescriptContents('interface A extends B.C {}')).body[0], 'file'))
.toEqual([]);
expect(logger.debug).toHaveBeenCalledWith(`Ignored an interface expression of unknown type ObjectExpression on A`);
expect(logger.debug).toHaveBeenCalledWith(`Ignored an interface expression of unknown type MemberExpression on A`);
});

it('should ignore on an interface that is extended anonymously, but still handle other interfaces', async() => {
it('should ignore on an interface that is extended via a qualified name, but handle others', async() => {
expect(loader.getSuperInterfaceNames(<any>(resolutionContext
.parseTypescriptContents('interface A extends B, {}, D {}')).body[0], 'file'))
.parseTypescriptContents('interface A extends B, C.D, E {}')).body[0], 'file'))
.toEqual([
{ value: 'B' },
{ value: 'D' },
{ value: 'E' },
]);
expect(logger.debug).toHaveBeenCalledWith(`Ignored an interface expression of unknown type ObjectExpression on A`);
expect(logger.debug).toHaveBeenCalledWith(`Ignored an interface expression of unknown type MemberExpression on A`);
});

it('should return on an interface with generics', async() => {
Expand Down Expand Up @@ -2585,6 +2585,26 @@ export = NS`,
));
});

it('for a single export from an unknown package', () => {
expect(loader.getClassElements('package', fileName, resolutionContext
.parseTypescriptContents(`export {A as B} from 'unknown-package'`)).exportedImportedElements)
.toEqual({});
expect(logger.warn).toHaveBeenCalledTimes(1);
expect(logger.warn).toHaveBeenLastCalledWith(expect.stringMatching(
/Ignoring invalid package "unknown-package": Could not resolve 'unknown-package' from path .*/u,
));
});

it('for export all from an unknown package', () => {
expect(loader.getClassElements('package', fileName, resolutionContext
.parseTypescriptContents(`export * from 'unknown-package'`)).exportedImportedAll)
.toEqual([]);
expect(logger.warn).toHaveBeenCalledTimes(1);
expect(logger.warn).toHaveBeenLastCalledWith(expect.stringMatching(
/Ignoring invalid package "unknown-package": Could not resolve 'unknown-package' from path .*/u,
));
});

it('for export all', () => {
expect(loader.getClassElements('package', fileName, resolutionContext.parseTypescriptContents(`export * from './lib/A'`)))
.toMatchObject({
Expand Down Expand Up @@ -2687,6 +2707,19 @@ export = NS`,
});
});

it('for a single export from file with string literal names', () => {
expect(loader.getClassElements('package', fileName, resolutionContext
.parseTypescriptContents(`export { 'A' as 'B' } from './lib/A'`)))
.toMatchObject({
exportedImportedElements: {
B: {
localName: 'A',
fileName: normalizeFilePath('dir/lib/A'),
},
},
});
});

it('for a single export object as reference', () => {
expect(loader.getClassElements('package', fileName, resolutionContext.parseTypescriptContents(`export = A`)))
.toMatchObject({
Expand Down
4 changes: 2 additions & 2 deletions test/parse/ParameterLoader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1601,12 +1601,12 @@ export interface A{
});

it('should error on an Array field type with no params', async() => {
await expect(async() => await getFieldRange('fieldA: Array<>', {}))
await expect(async() => await getFieldRange('fieldA: Array', {}))
.rejects.toThrow(new Error('Found invalid Array field type at field fieldA in A at file'));
});

it('should log on an Array field type with no params', async() => {
await expect(getFieldRange('fieldA: Array<>', {}, false)).resolves
await expect(getFieldRange('fieldA: Array', {}, false)).resolves
.toEqual({ type: 'wildcard' });
expect(logger.error).toHaveBeenCalledWith('Found invalid Array field type at field fieldA in A at file');
});
Expand Down
Loading
Loading