Skip to content

Commit

Permalink
fix(generator): try to detect automatically the routes definition (#2017
Browse files Browse the repository at this point in the history
)

## Proposed change

Try to detect automatically the routes definition

## Related issues

- 🐛 Fixes #1873
  • Loading branch information
matthieu-crouzet authored Aug 5, 2024
2 parents e364114 + c0d9519 commit 7d4b1e5
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 8 deletions.
4 changes: 3 additions & 1 deletion packages/@o3r/core/schematics/index.it.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ describe('new otter application', () => {
);
await addImportToAppModule(applicationPath, 'TestServiceBaseModule', 'src/services/test-service');

packageManagerExec({script: 'ng', args: ['g', '@o3r/core:page', 'test-page', '--app-routing-module-path', 'apps/test-app/src/app/app-routing.module.ts', ...appNameOptions]},
packageManagerExec({script: 'ng', args: ['g', '@o3r/core:page', 'test-page', ...appNameOptions]},
execAppOptions
);

Expand Down Expand Up @@ -118,6 +118,8 @@ describe('new otter application', () => {
expect(diff.added.filter((file) => new RegExp(path.posix.join(relativeApplicationPath, 'src/store').replace(/[\\/]+/g, '[\\\\/]')).test(file)).length).toBeGreaterThan(0);
expect(diff.added.filter((file) => new RegExp(path.posix.join(relativeApplicationPath, 'src/styling').replace(/[\\/]+/g, '[\\\\/]')).test(file)).length).toBeGreaterThan(0);

expect(diff.modified).toContainEqual(expect.stringMatching(new RegExp(path.posix.join(relativeApplicationPath, 'src/app/app.routes.ts').replace(/[\\/]+/g, '[\\\\/]'))));

expect(() => packageManagerRunOnProject(appName, isInWorkspace, {script: 'build'}, execAppOptions)).not.toThrow();

// should pass the e2e tests
Expand Down
83 changes: 83 additions & 0 deletions packages/@o3r/core/schematics/page/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ import { Tree } from '@angular-devkit/schematics';
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
import * as fs from 'node:fs';
import * as path from 'node:path';
const mockGetAppModuleFilePath = jest.fn();
jest.mock('@o3r/schematics', () => {
return {
...jest.requireActual('@o3r/schematics'),
getAppModuleFilePath: mockGetAppModuleFilePath
};
});

const collectionPath = path.join(__dirname, '..', '..', 'collection.json');

Expand Down Expand Up @@ -117,4 +124,80 @@ describe('Page', () => {
expect(tree.files.filter((file) => /test-page/.test(file)).length).toEqual(7);
});
});

describe('Automatic routes definition detection', () => {
beforeEach(() => {
mockGetAppModuleFilePath.mockReset();
mockGetAppModuleFilePath.mockClear();
});

it('should add the route on standalone application', async () => {
mockGetAppModuleFilePath.mockReturnValue('app.config.ts');
const initialTree = getInitialTree();
initialTree.create('app.config.ts', `
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { appRoutes as routes } from './app.routing.module';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes)
]
};
`);
const runner = new SchematicTestRunner('schematics', collectionPath);
const angularPackageJson = require.resolve('@schematics/angular/package.json');
runner.registerCollection('@schematics/angular', path.resolve(path.dirname(angularPackageJson), require(angularPackageJson).schematics));
tree = await runner.runExternalSchematic('schematics', 'page', {
projectName: 'test-project',
name: 'testPage',
prefix: 'o3r',
appRoutingModulePath: undefined,
path: '.'
}, initialTree);

expect(tree.readText('/app.routing.module.ts')).toContain('path: \'test-page\'');
});

it('should add the route on module application', async () => {
mockGetAppModuleFilePath.mockReturnValue('app.module.ts');
const initialTree = getInitialTree();
initialTree.create('app.module.ts', `
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/routing';
import { AppComponent } from './app.component';
const appRoutes: Routes = [
{
path: '',
children: [
{path: '', redirectTo: '/home', pathMatch: 'full'}
]
}
];
@NgModule({
declarations: [
AppComponent
],
imports: [
RouterModule.forRoot(appRoutes)
],
bootstrap: [AppComponent]
})
export class AppModule {}
`);
const runner = new SchematicTestRunner('schematics', collectionPath);
const angularPackageJson = require.resolve('@schematics/angular/package.json');
runner.registerCollection('@schematics/angular', path.resolve(path.dirname(angularPackageJson), require(angularPackageJson).schematics));
tree = await runner.runExternalSchematic('schematics', 'page', {
projectName: 'test-project',
name: 'testPage',
prefix: 'o3r',
appRoutingModulePath: undefined,
path: '.'
}, initialTree);

expect(tree.readText('/app.module.ts')).toContain('path: \'test-page\'');
});
});
});
32 changes: 31 additions & 1 deletion packages/@o3r/core/schematics/page/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
addImportToModuleFile,
applyEsLintFix,
createSchematicWithMetricsIfInstalled,
getAppModuleFilePath,
getDestinationPath,
getModuleIndex,
getWorkspaceConfig,
Expand Down Expand Up @@ -209,8 +210,37 @@ function ngGeneratePageFn(options: NgGeneratePageSchematicsSchema): Rule {
import: `./${indexFilePath.replace(/[\\/]/g, '/')}`,
module: `${pageName}${options.standalone ? 'Component' : 'Module'}`
};
if (options.appRoutingModulePath) {
return insertRoute(tree, context, options.appRoutingModulePath, route, options.standalone);
}
const appModuleFilePath = getAppModuleFilePath(tree, context, options.projectName);
if (appModuleFilePath) {
const text = tree.readText(appModuleFilePath);
const match = text.match(/(provideRouter|RouterModule\.forRoot)\((\s*)?(?<routeVarName>[^,\s)]*)/);
const routeVariableName = match?.groups?.routeVarName;
if (routeVariableName) {
const sourceFile = ts.createSourceFile(
appModuleFilePath,
text,
ts.ScriptTarget.ES2015,
true
);
const importStatement = sourceFile.statements.find((statement): statement is ts.ImportDeclaration =>
ts.isImportDeclaration(statement)
&& !!statement?.moduleSpecifier
&& ts.isStringLiteral(statement.moduleSpecifier)
&& !!statement.importClause?.namedBindings
&& ts.isNamedImports(statement.importClause.namedBindings)
&& statement.importClause.namedBindings.elements.some((element) => element.name.escapedText === routeVariableName)
);
const importRouteVariablePath = (importStatement?.moduleSpecifier as ts.StringLiteral | undefined)?.text;
// If importRouteVariablePath is undefined it is because the variable is defined in this file
const appRoutingModulePath = importRouteVariablePath ? path.join(path.dirname(appModuleFilePath), `${importRouteVariablePath}.ts`) : appModuleFilePath;

return insertRoute(tree, context, options.appRoutingModulePath, route, options.standalone);
return insertRoute(tree, context, appRoutingModulePath, route, options.standalone);
}
}
throw new O3rCliError('No routes definition found. Please use the option `appRoutingModulePath` to specify the path of the routes definition.');
};

return chain([
Expand Down
7 changes: 2 additions & 5 deletions packages/@o3r/core/schematics/page/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@
},
"appRoutingModulePath": {
"type": "string",
"description": "Application routing module path",
"default": "./src/app/app-routing.module.ts",
"x-prompt": "Application routing module path?"
"description": "Application routing module path"
},
"prefix": {
"type": "string",
Expand Down Expand Up @@ -74,7 +72,6 @@
},
"additionalProperties": true,
"required": [
"name",
"appRoutingModulePath"
"name"
]
}
2 changes: 1 addition & 1 deletion packages/@o3r/core/schematics/page/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export interface NgGeneratePageSchematicsSchema extends SchematicOptionObject {
scope: string;

/** Application routing module path */
appRoutingModulePath: string;
appRoutingModulePath?: string | undefined;

/** Selector prefix */
prefix?: string | undefined;
Expand Down

0 comments on commit 7d4b1e5

Please sign in to comment.