angular-cli icon indicating copy to clipboard operation
angular-cli copied to clipboard

Build process hook

Open fabienbranchel opened this issue 6 years ago • 19 comments

Bug Report or Feature Request (mark with an x)

- [X] feature request

Command (mark with an x)

- [X] build

Desired functionality

It would be nice to have a hook in the build process, between the index generation and the hash computation, for execute something.

I try to use the Critical lib with my Angular project. First, I build my app, then I use Critical script in order to inline my CSS in the index file : I rewrite my index file. It was messed up with Angular SW, so I submit an issue in Angular's Github (https://github.com/angular/angular/issues/25267).

@gkalpak explains that the issue result from the stored hash mismatch, because of the rewrite of index file.

fabienbranchel avatar Aug 06 '18 09:08 fabienbranchel

I'm in a similar situation, and this would be extremely useful.

aidionidis avatar Oct 23 '18 20:10 aidionidis

Recently I decided to try Angular CLI to control my whole project pipeline for the first time and there are already 2 cases where I need to be able to add hooks in the build process. I have a workspace with a couple of core libraries and some addons that depend on them. Here are my cases where I need hooks:

  1. After I build a core library I need to copy less files into dist folder of it so it will be bundled in the npm package (see https://github.com/angular/angular-cli/issues/11071)
  2. I noticed that if I do not write types explicitly they are added with a wrong path sometimes during compilations. I need a postbuild script to check that it didn't happen.

P.S. Here's a case where it is incorrectly added: readonly prop = SOME_CONST; — here SOME_CONST is imported from a library and in that library it has a type so TypeScript knows about the type of prop, it works fine until I do build. Then in the resulting .d.ts it turns into: readonly prop: import("../../../../../dist/lib/lib").Some_Type;

waterplea avatar Apr 12 '19 12:04 waterplea

This would be helpful.

My situation: I need to compile a TypeScript file and output the JavaScript to the top level in dist. I could do this with a separate build script in package.json, but that gets awkward when running the angular build in watch mode.

What I would like is a lifecycle hook from the angular.json that runs a shell command or a JavaScript file. Currently I would need it after the Angular build completes, but I would imagine for other people it would be helpful to have several lifecycle hooks.

I'm imagining something like:

  "buildHooks": {
    "afterBuild": [
      "npm run build-that-other-thing-and-copy-it-to-dist"
    ]
  }

It looks like there are other people facing this issue -- this answer on this Stack Overflow question suggests starting another parallel process along with ng build --watch that watches for changes to dist and runs another command whenever dist changes.

alexanderbird avatar May 17 '19 18:05 alexanderbird

Let me share my use-case. (copied from #16308)

In general, ng build is often aliased as npm run build. And for production build, often ng build --prod is aliased as a different command like npm run build:prod. Then my package.json becomes like below;

{
  "scripts": {
    "build": "ng build",
    "build:prod": "ng build --prod"
  }
}

In that case, for adding preprocess script, I have to add it into each scripts like this;

{
  "scripts": {
    "build": "npm run codegen && ng build",
    "build:prod": "npm run codegen && ng build --prod",
    "codegen": "..."
  }
}

I can use pre- hook of npm-scripts, however, a problem is there are two build scripts. It brings a lot of scripts in package.json.

{
  "scripts": {
    "prebuild": "npm run codegen",
    "build": "ng build",
    "prebuild:prod": "npm run prebuild",
    "build:prod": "ng build --prod",
    "codegen": "..."
  }
}

This problem can be more serious in a workspace having multiple projects. Each build commands have to take project identifier and often these are separated as different npm-scripts.

{
  "scripts": {
    "build:appA": "ng build appA",
    "build:appA:prod": "ng build appA --prod",
    "build:appB": "ng build appB",
    "build:appB:prod": "ng build appB --prod",
  }
}

Too many npm-scripts are hard to maintain. So I'd like to propose Angular CLI's command hook feature.

So I'd like to propose CLI command hook as a solution.

CLI command hook is a mechanism to execute a script before/after running CLI's command. It is not only for ng build but all command.

With the hook, pre-build code-generation is able to be configured in angular.json like below;

{
  "projects": {
    "appA": {
      "architect": {
        "build": {
          "hooks": {
            "pre": "npm run codegen"
          },
          "configurations": {
             "production": {
                "hooks": {
                   "pre": "npm run codegen:prod",
                 }
              }
           }
        }
      }
    }
  }
}

Once configuring this, it can work with --prod or any --configurations because each configuration can have its own hooks option to override. And any architect commands can be configured as well as build. Each architect author doesn't have to know about that.

lacolaco avatar Nov 28 '19 10:11 lacolaco

It's been a couple of months, unfortunately without updates here. Anything happening towards this feature request?

nicky-lenaers avatar May 15 '20 08:05 nicky-lenaers

Recently I decided to try Angular CLI to control my whole project pipeline for the first time and there are already 2 cases where I need to be able to add hooks in the build process. I have a workspace with a couple of core libraries and some addons that depend on them. Here are my cases where I need hooks:

  1. After I build a core library I need to copy less files into dist folder of it so it will be bundled in the npm package (see #11071)
  2. I noticed that if I do not write types explicitly they are added with a wrong path sometimes during compilations. I need a postbuild script to check that it didn't happen.

P.S. Here's a case where it is incorrectly added: readonly prop = SOME_CONST; — here SOME_CONST is imported from a library and in that library it has a type so TypeScript knows about the type of prop, it works fine until I do build. Then in the resulting .d.ts it turns into: readonly prop: import("../../../../../dist/lib/lib").Some_Type;

I'm sorry to jump on this relatively old comment now, but @waterplea isn't your 2. point directly related to TypeScript? More specifically to the following issue: https://github.com/microsoft/TypeScript/issues/32653.

Unfortunately tho, the issue is getting pushed from release to release and now to backlog since a while.

klemenoslaj avatar May 15 '20 09:05 klemenoslaj

The investigations are sometimes long.

VaultDeveloper avatar Sep 03 '20 09:09 VaultDeveloper

I would like to add to @lacolaco's request if I may.

It would be ideal if ng serve is able to run codegen. And then a way to define the dependencies of codegen so Angular CLI knows when to run it. And then a way of describing whether the rest of the build depends on codegen or not. For lacolaco's request, I believe the rest of the build depends on the output of codegen. But for my use case, I would be just concatenating some files which are truly independent of the rest of the build.

tongfa avatar Sep 22 '20 21:09 tongfa

My workaround is using angular-builders/custom-webaack and this webpack config:

module.exports = (config, argv) => {
  process.env.NODE_ENV = process.env.NODE_ENV || config.mode || argv.mode || 'development'

  config.plugins.push({
    apply: (compiler) => {
      compiler.hooks.entryOption.tap('<ANY_NAME_YOU_WANT_THIS_HOOK_TO_HAVE>', () => {
        // run pre scripts here
      })
    },
  })

  return config
}

muuvmuuv avatar Nov 19 '20 14:11 muuvmuuv

I am on the same boat. I need to generate some typescript files from json config files and I have to do it manually. It would be good to be able to integrate in the build process.

distante avatar Dec 05 '20 08:12 distante

I would also love to see pre-build hooks as a feature. It would be important that it happens via angular.json and not simple npm-scripts because if you have other tooling around like test-suits that directly use angular's build cli, its not possible to inject a call to an npm scripts in between.

kolja-ec avatar Apr 22 '21 10:04 kolja-ec

same as above! ☝️ 😅

Elviszip avatar May 06 '21 17:05 Elviszip

Another use case for pre-build script is a replacement for fileReplacements. Prior to Angular 11 components could be built with configuration dependent templates. The removal blocks the upgrade path from 10 to 11. See https://github.com/angular/angular-cli/issues/19390#issuecomment-745283960, for example. I have not found a workaround

stemarco avatar Jun 15 '21 15:06 stemarco

The pre-build hooks feature sounds great to me! Keep me posted if this happens!

ArthusLiang avatar Sep 07 '21 14:09 ArthusLiang

So @stemarco are you talking about replace other html files than index.html? I'm looking for the same and I haven't found any workaround in angular.json yet, I think the only way I've now is create a post build script...

BruneXX avatar Sep 17 '21 17:09 BruneXX

@BruneXX, yes, for example I have a layout component with two implementations, one for production and one for development that differ in their templates. I would like to swap in the production version automatically at prod build time. This was possible in early versions of Angular with fileReplacements in angular.json. Perhaps it was an unintended feature. I don't think that this can be done cleanly with a script.

stemarco avatar Sep 22 '21 15:09 stemarco

Just migrated from another bundler expecting this to be a thing... but it isn't. The Angular CLI leaves a lot to be desired.

jpike88 avatar Jan 14 '22 11:01 jpike88

Came across this bug today and had a short discussion about it within the team. Looking through the comments it seems like most of the use cases mentioned have since been addressed (critical CSS inlining, TS bugs, exporting styles from libraries, etc.)

The use cases I see that still don't have good solutions are:

  1. Codegen of source files to use in an Angular compilation (protocol buffers, JSON -> TS, etc.)
  2. Pre/post build steps which generate or transform files and want to work with watch mode (generating a JSON asset, generating a custom index page, etc.)

Build hooks are an easy and straightforward solution, but can also be tricky to manage and hard for the CLI to reason about. For example, a pre-build script would always have to be executed and can never be cached because the CLI doesn't know what the script is doing, what it depends on, or when it should be running.

We've had some rough discussions about adding a dependency structure to angular.json which would allow one builder to depend on the output of another. A codegen or pre-build step could then be done through a custom (or community-implemented) builder that gets depended upon by the normal Angular browser build. A post-build step could be done through a similar builder which depends on the Angular browser build and modifies / adds more files.

This would give the CLI more insight into the structure of the build and allow it to more effectively support watch mode and skip building dependencies when nothing has changed. We're probably still a little ways out from designing and implementing such a system. There's also a lot of usability improvements we'll likely need for builders to make build customizations easy to do. However, this seems to us like the right approach for tackling this problem.

dgp1130 avatar Mar 10 '22 20:03 dgp1130

I have just come across this issue and I see it has been open since August 2018, which is a pretty long time. Essentially I know these pre and post script hooks work normally in a standard nodejs app, not sure why it doesn't work here in angular. I have two directories, 1 for backend(nodejs) and 1 for frontend(angular), in my frontend directory, I have my build scripts like so in package.json:

  "scripts": {
    "build": "ng build",
    "postbuild": "cp -a dist/. ../backend/public/",
  },

However, when I run the ng b command, only the main build script works.

I then tried setting the scripts like so:

  "scripts": {
    "prebuild": "ng build",
    "build": "cp -a dist/. ../backend/public/",
  },

The same outcome (only the main build script works). Lastly, I tried:

  "scripts": {
    "build": "ng build && cp -a dist/. ../backend/public/",
  },

which are all meant to have the same effect, but none of them could still run the cp command, only the build command works.

I would appreciate it if the angular team can maybe work on this? or maybe have a nice workaround handy as I do not want to always copy over manually.

chunkingz avatar Mar 18 '22 03:03 chunkingz

any news about an eventual hooks implementation ?

t0m-4 avatar Jun 02 '23 08:06 t0m-4

My usecase is that I have my backend written in a different language (rust) and the api bindings generation for typescript can be triggered by executing a specific external command.

Unfortunately rust/cargo does not support post-build scripts, and angular doesn't seem to support pre-build scripts, so I have to manually invoke the bindings generation.

I'd like to be able to set up a hook that is triggered when one of the watched directories (which includes the source folder for the rust bindings) change, and if they change the outputting typescript files that would trigger a rebuild of the angular project as well.

Btw, this could be a solution for #25699 as well, if there would be a hook before compilation that the user could set to clear the screen.

axos88 avatar Aug 20 '23 19:08 axos88

my use case for this would be:

  • add the git commit id and timestamp to a file in dist to know for sure at what point it was compiled
  • make sure a proper release was created and the version number changed
  • add a file with the current version to dist (i want to show the version to the clients, but as far as i know, angular does not have a way of doing this)

acognigni-evotecnia avatar Aug 22 '23 15:08 acognigni-evotecnia

@acognigni-evotecnia you can do this already with webpack-define but yes its not ideal when looking at esbuild.

muuvmuuv avatar Aug 22 '23 17:08 muuvmuuv