Angular - How to exclude a lazy loaded module from prod build?
Asked Answered
S

2

5

I have an angular module for demo purposes (DevShowcaseModule). This module should not be included in the production build. In order to hide this demos from the endusers and prevent demo code errors in the production.

Environment:

  • Angular Version: 7.2.5
  • Angular CLI: 7.3.2

This is my app-routing.module.ts

{
    path: APP_NAV_ITEMS.DEV_SHOWCASE,
    canActivate: [ AuthGuard ],
    loadChildren: './_dev-showcase/dev-showcase.module#DevShowcaseModule',
}

I have tried to exclude the module folder from the tsconfig.json. But it doesnt work, i can still call the route and the demo module is loaded.

{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "importHelpers": true,
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "module": "es2015",
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,

    "noUnusedLocals": true,
    "noUnusedParameters": true,

    "target": "es5",
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2018",
      "dom"
    ]
  },
  "exclude": [
    "app/_dev-showcase/*"

  ]
}

Any idea how to do it properly?

Thanks!

Stuppy answered 9/5, 2019 at 12:12 Comment(15)
Either dynamically build routes or guard the dev route with a route guard.Hoyle
use angular.io/api/router/CanLoadChristian
CanLoad - Decides if a module can be loaded lazily, Controls if a route can even be loaded. This becomes useful for feature modules that are lazy loaded. They won’t even load if the guard returns false.Christian
@ritaj Hi, thank you. I saw that solution, but If i do it over the routes, is the DevShowcaseModule code included in the prod build? I want DevShowcaseModule and his components completely removed from the prod build code.Stuppy
oh i see , if you don't want the code use this solution : hackernoon.com/…Christian
@JoelJoseph Thank you also for the idea, but same question like for ritaj. If i do it over the routes, is the DevShowcaseModule code included in the prod build?Stuppy
@JoelJoseph ok thanks, i will take a look at the article.Stuppy
It's not included in prod build, no lazy loaded modules are included anywhere untill their routes are activated.Hoyle
@ritaj ...sounds weird, how should a module be loaded (lazy) if the code for that module is not included in the prod build?Stuppy
if you are importing the modules it means it will be added in the production build . if not specified to exclude it. You can control how it loads . it has to be part of the bundle to be lazy loadedChristian
Its all about the demo module. Its not imported anywhere besides the lazy loading over the route. Thats the only place where the demo module is mentioned. In the dev we want it to be loaded, doenst matter if lazy or not. But in the prod, we dont want any piece of that demo code.Stuppy
@Stuppy the method you specified only worked until Angular 4 i guess . after migration to angular 6 , TS seems to build any files it sees an 'import' statement for regardless of if it's in the 'exclude' property in tsconfig.json. Controlling the import seems to be the only working option as i mentioned aboveChristian
Because that's the point of lazy loading. You don't send the code over the internet to the browser if the client will not use it. So if the route is never activated, module is never loaded.Hoyle
@ritaj Yes you are right , but he want the code to be excluded to reduce the build size while delivering the build file. So lazy loading wont help him. he will have to control the importChristian
@JoelJoseph hmm thats bad. ok, thank you. I try to make the entry for the demo module in the app-routing.module.ts conditionally if possible. This seems to be the best solution.Stuppy
P
9

I think you can leverage CLI fileReplacements feature like:

angular.json

"configurations": {
  "production": {
    "fileReplacements": [
      ...
      {
        "replace": "src/app/demo.routes.ts",
        "with": "src/app/demo.routes.prod.ts"
      }
    ],

demo.routes.ts

import { Routes } from '@angular/router';

export const demoRoutes: Routes = [
  {
    path: 'demo',
    loadChildren: './demo/demo.module#DemoModule'
  }
];

demo.routes.prod.ts

export const demoRoutes = [];

The root router configuration should look like:

import { demoRoutes } from './demo.routes';

RouterModule.forRoot([
  {
    path: '',
    component: HomeComponent
  },
  ...demoRoutes
])

Using this method the cli will only bundle the DemoModule in dev mode.

Pablopabon answered 9/5, 2019 at 13:58 Comment(4)
Hello yurzui, thats exactly what i have planned to do, based on the suggestions from the comments above. Awesome, this is really great. Thank you very much for the complete code snippet! I will try it tomorrow...Stuppy
Hello yurzui, i have found an easier way to achieve the same result. But with less code and easier understanding for other developers. I have updated your answer. Correct me if it was the wrong way or placement.Stuppy
I think in your example on the first run cli won't be able to find lazy module so it won't be bundled because it should be statically analyzed. But on the reload(as soon as you make any changes) it will work monosnap.com/file/dg1ijVIWo1MhrrCUgzUgRm0fW1rzD2Pablopabon
Thanks. Thats true, my first test worked like you described. But in my second test i cant manage it to load the DemoModule in the dev at all. And i really would like to understand why it is working once after one reload and then even after few reloads not at all.Stuppy
S
0

This is another possible solution. PROD Mode is working fine.
The only disadvantage is, DemoModule is not loaded after ng serve. It needs a code change to start the compiler, afterwards DemoModule is loaded.

Is there any chance to load the lazy loaded DemoModule without a code change?

if(!environment.production) {
    console.log('Application is running in dev mode');
    routes.unshift(
        {
            path: APP_NAV_ITEMS.DEV_SHOWCASE,
            canActivate: [ AuthGuard ],
            loadChildren: './_demo/demo.module#DemoModule',
        },
    )
}else{
    console.log('Application is running in production mode');
}

@NgModule({
    imports: [ RouterModule.forRoot(routes, { useHash: true }) ],
    exports: [ RouterModule ]
})
export class AppRoutingModule {
}
Stuppy answered 10/5, 2019 at 15:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.