Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Does not work - produced paths for require are incorrect. #20

Open
MarkKharitonov opened this issue Aug 24, 2016 · 10 comments
Open

Does not work - produced paths for require are incorrect. #20

MarkKharitonov opened this issue Aug 24, 2016 · 10 comments

Comments

@MarkKharitonov
Copy link

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'
        })
    ]
};

@jorawarsingh
Copy link

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.

@MarkKharitonov
Copy link
Author

MarkKharitonov commented Aug 24, 2016

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.

@TheLarkInn
Copy link
Owner

TheLarkInn commented Aug 25, 2016

@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
Copy link
Owner

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.

@MarkKharitonov
Copy link
Author

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').

@TheLarkInn
Copy link
Owner

TheLarkInn commented Aug 25, 2016

No also, this is the relative path strategy for the angular AoT compiler as well. See angular/angular#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.

@MarkKharitonov
Copy link
Author

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

@Gillardo
Copy link

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

@ajohnsonRH
Copy link

Has someone found a fix for this yet?

@sujithuix
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants