angular2-template-loader icon indicating copy to clipboard operation
angular2-template-loader copied to clipboard

Does not work - produced paths for require are incorrect.

Open MarkKharitonov opened this issue 8 years ago • 10 comments

Please, observe the following TypeScript file:

import { Component } from "@angular/core";

@Component({
    selector: 'app-root',
    templateUrl: './app/app.component.html'
})
export class AppComponent {
}

And it works fine before I use angular2-template-loader. When I am trying to use the latter and run webpack it fails with the following message:

    polyfills.js     283 kB       1  [emitted]  polyfills
       vendor.js    3.32 MB       2  [emitted]  vendor
     main.js.map    17.2 kB       0  [emitted]  main
polyfills.js.map     353 kB       1  [emitted]  polyfills
   vendor.js.map    3.94 MB       2  [emitted]  vendor
      index.html  607 bytes          [emitted]
    + 912 hidden modules

ERROR in ./src/client/app/app.component.ts
Module not found: Error: Cannot resolve 'file' or 'directory' ./app/app.component.html in C:\Users\markk\IdeaProjects\QuestionarySample2\src\client\app
 @ ./src/client/app/app.component.ts 18:22-57

...
Child html-webpack-plugin for "index.html":
        + 1 hidden modules

C:\Users\markk\IdeaProjects\QuestionarySample2>

(there are more errors like that for other components, I replaced them with an ellipsis)

The loader converts the aforementioned source into:

import { Component } from "@angular/core";

@Component({
    selector: 'app-root',
    template: require('./app/app.component.html')
})
export class AppComponent {
}

And now it fails.

As I understand, the reason is that require works off the current directory, whereas the templateUrl is relative to the root. So, './app/app.component.html' is correct when taken relative to the root, but wrong when relative to the current location.

It is worth noting, that I do not use require anywhere in the source code, only the ES6 import statement, which is also relative to the current location. It is only the Angular2 templateUrl and styleUrls that are relative to the root.

Anyway, I must be doing something wrong, because nobody seems to have my problem. But what am I doing wrong?

The file name is src/client/app/app.component.ts. The source code directory structure is:

C:.
│   .gitignore
│   karma.conf.js
│   package.json
│   tsconfig.json
│   tslint.json
│   typings.json
│   webpack.config.js
│           
├───config
│       karma-test-shim.js
│       karma.conf.js
│       webpack.common.js
│       webpack.dev.js
│       webpack.prod.js
│       webpack.scratch.js
│       webpack.test.js
│       
└───src
    ├───client
    │   │   global.css
    │   │   index.html
    │   │   main.ts
    │   │   polyfills.ts
    │   │   tsconfig.json
    │   │   vendor.ts
    │   │   
    │   └───app
    │       │   app.component.html
    │       │   app.component.ts
    │       │   app.module.ts
    │       │   app.routing.ts
    │       │   settings.component.ts
    │       │   signout.component.ts
    │       │   
    │       ├───assets
    │       │       ...
    │       │       
    │       ├───questionnaire
    │       │       ...
    │       │       
    │       └───shared
    │               ...
    │               
    └───server
            api.ts
            main.ts
            tsconfig.json         

The webpack configuration is:

webpack.config.js

module.exports = require('./config/webpack.dev.js');

./config/webpack.dev.js

var webpackMerge = require('webpack-merge');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var commonConfig = require('./webpack.common.js');
const path = require('path');

module.exports = webpackMerge(commonConfig, {
    // devtool: 'cheap-module-eval-source-map',
    devtool: 'source-map',

    output: {
        publicPath: `http://localhost:${commonConfig.port}/`,
        filename: '[name].js',
        sourceMapFilename: '[name].js.map',
        chunkFilename: '[id].chunk.js'
    },

    plugins: [
        new ExtractTextPlugin('[name].css')
    ],

    devServer: {
        inline: true,
        hot: true,
        progress: true,
        port: commonConfig.port,
        proxy: {
            '/api*': {
                target: `http://localhost:${commonConfig.port - 1}`
            }
        },
        historyApiFallback: true,
        watchOptions: {
            aggregateTimeout: 300,
            poll: 1000
        }
    }
});

./config/webpack.common.js

var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
const path = require('path');

module.exports = {
    port: process.env.PORT || 3000,

    entry: {
        polyfills: './src/client/polyfills',
        vendor: './src/client/vendor',
        main: './src/client/main'
    },

    output: {
        path: path.join(__dirname, '../dist/client'),
    },

    resolve: {
        extensions: ['', '.js', '.ts']
    },

    module: {
        loaders: [
            {
                test: /\.ts$/,
                loaders: ['ts', 'angular2-template-loader']
            },
            {
                test: /\.html$/,
                // loader: 'html'
                loader: 'raw'
            },
            {
                test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
                loader: 'file?name=assets/[name].[hash].[ext]'
            },
            // {
            //     test: /\.css$/,
            //     exclude: path.join(__dirname, '../src/client/app'),
            //     loader: ExtractTextPlugin.extract('style', 'css?sourceMap')
            // },
            {
                test: /\.css$/,
                // exclude: path.join(__dirname, '../src/client/app'),
                loader: 'raw'
            }
        ]
    },

    plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            name: ['main', 'vendor', 'polyfills']
        }),

        new HtmlWebpackPlugin({
            template: 'src/client/index.html'
        })
    ]
};

MarkKharitonov avatar Aug 24 '16 16:08 MarkKharitonov

It's not angular2-template-loader which is complaining it's webpack. i have post an answer on your question on stackoverflow you can find explanation there.

jorawarsingh avatar Aug 24 '16 17:08 jorawarsingh

The alternative you give me is as follows:

  1. When using templateUrl and styleUrls in Angular2 use paths relative to the current directory. At this point Angular2 is no longer able to find the html and css associated with the component. The code is broken.
  2. Make sure to use this loader, otherwise the produced code will not run, because it is broken - see 1.

If this is my choice, then I will just use require myself and I do not need this loader.

MarkKharitonov avatar Aug 24 '16 17:08 MarkKharitonov

@jorawarsingh is correct, your path can simply be expressed as './app.component.html'. If you'd like to use the flat require that is fine also.

I mean, the whole point of this loader is to not have angular try and make hundreds of requests to access your templates at runtime, so avoiding using their non-cannonical resolution strategy is a given.

Also you could create a custom resolver plugin to emulate this behavior instead when a request is being called for x.component.html, and alias it to be ./app/x.component.html if it's that important to you.

TheLarkInn avatar Aug 25 '16 16:08 TheLarkInn

I would also be more than happy to merge a PR that adds a loader option called useAngularResolution that will switch this behavior based upon the option. Would need to have tests and coverage included ofcourse.

TheLarkInn avatar Aug 25 '16 16:08 TheLarkInn

Angular2 expects templateUrl: './app/app.component.ts'. This is how we write it in Angular 2. What you guys are telling me that I should be writing templateUrl: './app.component.ts'. This will only work if I use this loader later on. As is the component is broken.

I think it is wrong. This loader carries angular2 in its name. For me it means it should use "angular resolution" by default. I.e. rewrite templateUrl: './app/app.component.ts' into template: require('./app.component.ts').

MarkKharitonov avatar Aug 25 '16 17:08 MarkKharitonov

No also, this is the relative path strategy for the angular AoT compiler as well. See https://github.com/angular/angular/issues/8953 for more information. Like I said, I'm more than happy to accept a PR for an added feature.

Candidly speaking maintaining webpack, working on the angular-cli is more of a priority right now, than edge cases. I hope you can understand.

TheLarkInn avatar Aug 25 '16 18:08 TheLarkInn

I do understand. Thank you for your work and effort.

MarkKharitonov avatar Aug 25 '16 18:08 MarkKharitonov

Can someone tell me how to resolve this? I agree that i do not see why i should have to change all my templateUrls when using this loader should really do it for me? What happens if later on down the line i do not want to use this loader, i will have to go all through my code and change the templateUrls

Gillardo avatar Oct 14 '16 08:10 Gillardo

Has someone found a fix for this yet?

ajohnsonRH avatar Nov 02 '16 19:11 ajohnsonRH

Anyone found a solution for this? spent days with no luck. Any input will be appreciated.

sujithuix avatar Mar 03 '17 12:03 sujithuix