Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(filter) : remove imports of filtered types #683

Merged
merged 4 commits into from
Aug 11, 2023
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
55 changes: 52 additions & 3 deletions packages/concerto-core/lib/basemodelmanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -741,10 +741,59 @@ class BaseModelManager {
*/
filter(predicate){
const modelManager = new BaseModelManager({...this.options}, this.processFile);
const filteredModels = Object.values(this.modelFiles)
.map((modelFile) => modelFile.filter(predicate, modelManager))
const removedFqns = []; // the list of FQN of types that have been removed

// remove the types from model files, populating removedFqns
let filteredModels = Object.values(this.modelFiles)
.map((modelFile) => modelFile.filter(predicate, modelManager, removedFqns))
.filter(Boolean);
modelManager.addModelFiles(filteredModels);

// remove concerto model files - as these are automatically added
// when we recreate the model manager below
filteredModels = filteredModels.filter(mf => !mf.isSystemModelFile());

// now update filteredModels to remove any imports of removed types
const modelsWithValidImports = filteredModels.map( modelFile => {
const ast = modelFile.getAst();
let modified = false;
removedFqns.forEach( removedFqn => {
const ns = ModelUtil.getNamespace(removedFqn);
const isSystemImport = ns.startsWith('concerto@') || ns === 'concerto';
if(!isSystemImport && modelFile.getImports().includes(removedFqn)) {
const removeName = ModelUtil.getShortName(removedFqn);
const removeNamespace = ModelUtil.getNamespace(removedFqn);
ast.imports = ast.imports.filter(imp => {
const remove = ModelUtil.getShortName(imp.$class) === 'ImportType' &&
imp.name === removeName &&
imp.namespace === removeNamespace;
if(remove) {
modified = true;
}
return !remove;
});
ast.imports.forEach( imp => {
if(imp.namespace === removeNamespace) {
if(ModelUtil.getShortName(imp.$class) === 'ImportTypes') {
imp.types = imp.types.filter((type) => {
const remove = (type === removeName);
if(remove) {
modified = true;
}
return !remove;
});
}
}
});
}
});
if(modified) {
return new ModelFile(this, ast, undefined, modelFile.fileName);
}
else {
return modelFile;
}
});
modelManager.addModelFiles(modelsWithValidImports);
return modelManager;
}
}
Expand Down
18 changes: 15 additions & 3 deletions packages/concerto-core/lib/introspect/modelfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,8 @@ class ModelFile extends Decorated {
/**
* Returns the types that have been imported into this ModelFile.
*
* @return {string[]} The array of imports for this ModelFile
* @return {string[]} The array of fully-qualified names for types imported by
* this ModelFile
*/
getImports() {
let result = [];
Expand Down Expand Up @@ -830,11 +831,22 @@ class ModelFile extends Decorated {
*
* @param {FilterFunction} predicate - the filter function over a Declaration object
* @param {ModelManager} modelManager - the target ModelManager for the filtered ModelFile
* @param {string[]} removedDeclarations - an array that will be populated with the FQN of removed declarations
* @returns {ModelFile?} - the filtered ModelFile
* @private
*/
filter(predicate, modelManager){
const declarations = this.declarations?.filter(predicate).map(declaration => declaration.ast);
filter(predicate, modelManager, removedDeclarations){
let declarations = []; // ast for all included declarations
this.declarations?.forEach( declaration => {
const included = predicate(declaration);
if(!included) {
removedDeclarations.push(declaration.getFullyQualifiedName());
}
else {
declarations.push(declaration.ast);
}
} );

const ast = {
...this.ast,
declarations: declarations,
Expand Down
29 changes: 29 additions & 0 deletions packages/concerto-core/test/modelmanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -1072,5 +1072,34 @@ concept Bar {

filtered.validateModelFiles();
});

it('should remove imports for filtered types', () => {
modelManager.addCTOModel(`namespace [email protected]
concept Used {}
concept Unused {}
`, 'child.cto', true);

modelManager.addCTOModel(`namespace [email protected]
concept AlsoUsed {}
`, 'cousin.cto', true);

modelManager.addCTOModel(`namespace [email protected]
concept Orphan {}
`, 'orphan.cto', true);

modelManager.addCTOModel(`namespace [email protected]
import [email protected]
import [email protected]
import [email protected]
import [email protected].{Used,Unused}
concept Person {
o Used used
o AlsoUsed alsoUsed
}
`, 'test.cto');
const filtered = modelManager.filter(declaration =>
['[email protected]','[email protected]','[email protected]', '[email protected]'].includes(declaration.getFullyQualifiedName()));
filtered.validateModelFiles();
});
});
});