Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular-cli build folder structure

The default angular-cli build generates dist folder with very flat structure - assets folder and js, html files. Is there a way to create fe. scripts folder and put all js files in it during build process?

like image 255
deviant Avatar asked Feb 28 '17 10:02

deviant


1 Answers

The CLI does not fully support this yet, but it does provides some helpful tools to get you much of the way there. I needed this to deploy my Angular application along with a WAR while trying to minimize the amount of complexity added via servlet-mappings. This question doesn't mention J2EE specifically, but I think that principles from this solution can be shared across various environments.


Relevant ng build parameters:

  • --deploy-url: The path mapping which will be applied to resources. This is probably as close as Angular-CLI currently gets to what you're looking for.
  • --output-path: Designates an output directory for the Angular build (in particular, I used this to designate unique output directories for different environments, since the application is being deployed to multiple targets with different configurations).
  • --base-href: The root URI for the application. E.g., /angular-app/ for http://localhost:4200/angular-app/. This sets the <base href="<uri>"> head tag and is necessary for non-hash routing. It might be necessary for other things, too.

The most interesting one for you is probably --deploy-url. If you set a deploy URL of /dist/, for example, then the references to JS files in your built application will be prefixed with /dist/.


Two big catches to this method:

  • --deploy-url does not change the output path for the JS files. It only changes the references to the JS files. The files themselves will still be put in the root build directory. You'll have to add a step to your build process to fix this manually.
  • --deploy-url does not seem to work for any other assets. I place all of my other assets in the assets/ directory, and the built output still refers to the assets via assets/<path> (rather than dist/assets/<path> as desired). You can work around this by providing a virtual directory or a URL rewrite.

For reference, here is my resulting directory structure for the WAR:

app/
    dist/    <-- Deployed Angular application
        assets/
            (images, CSS, etc)
        *.js
        (other assets pulled into the root path, e.g. *.(svg|eot|woff|woff2|ttf) from Font Awesome)
        index.html
    WEB-INF/
    ...
    index.jsp

These are the steps I take to produce this structure:

  1. ng build -pr false --prod --output-path build/node-prod --base-href /angular-app/ --deploy-url /angular-app/dist/.
  2. Copy contents of build/node-prod to the WAR build directory's app/dist via a gradle task.
  3. Point index.jsp to index.html via the content <%@include file="dist/index.html"%>.
  4. Add a servlet-mapping for the default servlet (static assets) for the paths /dist/* and /assets/*.
  5. Add URL-mapping via http://tuckey.org/urlrewrite/ for ^/assets/(.*)$ to /dist/assets/$1 (or via httpd, nginx, etc.). This is needed because of the catch given above for --deploy-url not working for other assets.
  6. (Optional) add 301 or 302 redirect for ^/dist/index.html$ to the root context path to prevent users accessing the app via the dist URI.

In the resulting webapp, http://localhost/angular-app/ is the only valid endpoint to my application. This endpoint points to index.jsp, which includes the content of index.html, which loads the relevant JS via <script src="/angular-app/dist/<some-file>.js"></script> tags.

When some other asset is necessary, such as the logo image, the page makes a request to assets/<file-name>, which is rewritten server-side via Tuckey to dist/assets/<file-name>, transparently resolving to the requested asset.

The nice thing about this solution was that we were able to deploy the Angular application in the root context without having to put everything that ng build built into the root WAR path. This is especially nice because we don't want to add global servlet mappings (e.g., *.js) when they can be avoided.

like image 111
Mike Hill Avatar answered Sep 24 '22 17:09

Mike Hill