0

I am migrating an application to Angular 13 and webpack 5 (+ dev server).

After migration to these versions, application is failing to start.

app.main.ts:

platformBrowserDynamic().bootstrapModule(AppModule)
.then((success) => console.log(`Application started`))
.catch((err) => console.error(err));

Whenever bootstrapModule(AppModule) is run, I get the following error in browser console, instead of module bootstrapping successfully:

TypeError: this.input.charCodeAt is not a function
    at PlainCharacterCursor.charAt (compiler.mjs?a6f7:13874:1)
    at PlainCharacterCursor.updatePeek (compiler.mjs?a6f7:13893:1)
    at PlainCharacterCursor.init (compiler.mjs?a6f7:13852:1)
    at new _Tokenizer (compiler.mjs?a6f7:13119:1)
    at tokenize (compiler.mjs?a6f7:13066:1)
    at HtmlParser.parse (compiler.mjs?a6f7:14068:1)
    at HtmlParser.parse (compiler.mjs?a6f7:14456:1)
    at parseTemplate (compiler.mjs?a6f7:18533:1)
    at parseJitTemplate (compiler.mjs?a6f7:19697:1)
    at CompilerFacadeImpl.compileComponent (compiler.mjs?a6f7:19466:43)
eval @ app.main.ts?170f:10

I have tried suggestions from Uncaught TypeError: this._input.charCodeAt is not a function, but they did not help me. Template URLs were named correctly and raw-loader is not used in my dev webpack configuration.

I have added try/catch to @angular/compiler/fesm2020/compiler.mjs _Tokenizer constructor like so:

        try {
            // original code of constructor
        } catch (exc) {
            console.log('[Tokenizer Error]: ' + _file && JSON.stringify(_file, null, 2));
            throw(exc);
        }

And got the following result:

{
  "content": {
    "default": "html content of main component"
  },
  "url": "ng:///MainComponent/template.html"
}

I see that in PlainCharacterCoursor input is assigned like so: this.input = fileOrCursor.content;. Later this input is used in charAt like so:

    charAt(pos) {
        return this.input.charCodeAt(pos);
    }

It seems to me that the issue here is content being a nested object with default field containing HTML content, instead of content being a key with a string value of said HTML content.

For development, I run: yarn run webpack serve -- --stats-error-details --config {webpack_config} --progress --profile

List of dependencies in package.json:

...
  "dependencies": {
    "@angular/animations": "^13.3.12",
    "@angular/cdk": "13.3.9",
    "@angular/common": "13.3.12",
    "@angular/compiler": "13.3.12",
    "@angular/core": "13.3.12",
    "@angular/forms": "13.3.12",
    "@angular/material": "^13.3.9",
    "@angular/platform-browser": "13.3.12",
    "@angular/platform-browser-dynamic": "13.3.12",
    "@angular/router": "13.3.12",
    "@fortawesome/angular-fontawesome": "^0.10.2",
    "@fortawesome/fontawesome-svg-core": "^6.2.0",
    "@fortawesome/free-solid-svg-icons": "^6.2.0",
    "@ng-bootstrap/ng-bootstrap": "^11.0.1",
    "@ngx-translate/core": "^14.0.0",
    "@ngx-translate/http-loader": "^7.0.0",
    "@popperjs/core": "^2.11.6",
    "bootstrap": "5.2.3",
    "brace": "^0.11.1",
    "chartist": "^1.3.0",
    "core-js": "3.26.1",
    "fast-xml-parser": "^4.0.11",
    "font-awesome": "4.7.0",
    "jquery": "3.6.1",
    "ng-jhipster": "0.16.0",
    "ng2-ckeditor": "1.3.6",
    "ngx-cookie": "6.0.0",
    "ngx-infinite-scroll": "13.0.2",
    "ngx-webstorage": "9.0.0",
    "postcss": "8.4.14",
    "reflect-metadata": "0.1.13",
    "rxjs": "^7.4.0",
    "sass": "^1.59.3",
    "save": "^2.9.0",
    "swagger-ui": "2.2.10",
    "tether": "2.0.0",
    "zone.js": "~0.11.8"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "~13.3.11",
    "@angular-eslint/builder": "13.5.0",
    "@angular-eslint/eslint-plugin": "13.5.0",
    "@angular-eslint/eslint-plugin-template": "13.5.0",
    "@angular-eslint/schematics": "13.5.0",
    "@angular-eslint/template-parser": "13.5.0",
    "@angular/cli": "13.3.11",
    "@angular/compiler-cli": "^13.3.12",
    "@ngtools/webpack": "13.3.11",
    "@types/ckeditor": "4.9.10",
    "@types/jasmine": "4.3.1",
    "@types/jquery": "^3.5.16",
    "@types/nanoscroller": "^0.8.6",
    "@types/node": "18.15.11",
    "@typescript-eslint/eslint-plugin": "5.27.1",
    "@typescript-eslint/parser": "5.27.1",
    "angular2-template-loader": "0.6.2",
    "browser-sync": "2.29.0",
    "browser-sync-webpack-plugin": "2.3.0",
    "copy-webpack-plugin": "11.0.0",
    "css-loader": "6.7.3",
    "dotenv-webpack": "^8.0.1",
    "eslint": "^8.17.0",
    "eslint-webpack-plugin": "^4.0.0",
    "exports-loader": "4.0.0",
    "file-loader": "6.2.0",
    "generator-jhipster": "7.9.3",
    "html-loader": "4.2.0",
    "html-webpack-plugin": "5.5.0",
    "jasmine-core": "4.5.0",
    "karma": "6.4.1",
    "karma-chrome-launcher": "3.1.1",
    "karma-coverage": "2.2.0",
    "karma-intl-shim": "1.0.3",
    "karma-jasmine": "5.1.0",
    "karma-junit-reporter": "2.0.1",
    "karma-notify-reporter": "1.3.0",
    "karma-phantomjs-launcher": "1.0.4",
    "karma-remap-istanbul": "0.6.0",
    "karma-sourcemap-loader": "0.4.0",
    "karma-webpack": "5.0.0",
    "merge-jsons-webpack-plugin": "2.0.1",
    "mini-css-extract-plugin": "2.7.2",
    "phantomjs-prebuilt": "2.1.16",
    "postcss-loader": "7.1.0",
    "proxy-middleware": "0.15.0",
    "rimraf": "3.0.2",
    "sass-loader": "^13.2.1",
    "sourcemap-istanbul-instrumenter-loader": "0.2.0",
    "style-loader": "3.3.2",
    "to-string-loader": "1.2.0",
    "ts-loader": "9.4.2",
    "tslib": "2.4.1",
    "typescript": "4.6.4",
    "webpack": "5.76.2",
    "webpack-cli": "5.0.1",
    "webpack-dev-server": "4.13.1",
    "webpack-merge": "5.8.0",
    "webpack-notifier": "1.15.0",
    "workbox-webpack-plugin": "6.5.4",
    "write-file-webpack-plugin": "4.5.1",
    "xml2js": "0.4.23"
  },
...

Webpack dev server configuration:

    devServer: {
        static: './target/www',
        port: 9060,
        devMiddleware: {
            writeToDisk: true
        },
        proxy: [{
            context: [
                /* jhipster-needle-add-entity-to-webpack - JHipster will add entity api paths here */
                '/api',
                '/management',
                '/swagger-resources',
                '/v2/api-docs',
                '/h2-console',
                '/auth'
            ],
            target: 'http://127.0.0.1:8080',
            secure: false
        }],
        watchFiles: {
            options:{
                ignored: /node_modules/
            }
        }
    },

Webpack's configuration parts related to HTML loading:

const HtmlWebpackPlugin = require('html-webpack-plugin');
...
module: {
    rules: [
        {
            test: /\.html$/,
            use: [
                {
                    loader: 'html-loader',
                    options: {
                        minimize: false
                    }
                }
            ],
            exclude: [path_to_index_html_here]
        }
    ]
}
...
plugins: [
    new HtmlWebpackPlugin({
            template: path_to_index_html_here,
            inject: 'body'
    })
]

If it is of any use, here is also loading of TS files with angular2-template:

rules: [{
        test: /\.ts$/,
        use: [
            {
                loader: 'ts-loader'
            },
            {
                loader: 'angular2-template-loader'
            }
        ],
        exclude: [path_to_generator_jhipster_in_modules]
    }
]

1 Answers1

1

Webpack 5 introduced breaking changes to the way it handles CommonJS imports (see https://webpack.js.org/loaders/html-loader/ for reference).

With your current configuration your html templates are loaded as modules instead of plain text.

One way would be to use the raw-loader (like suggested in the other stackoverflow). However, Webpack 5 also introduced a new assets module as a replacement for raw-loader/file-loader/url-loader (see https://webpack.js.org/guides/asset-modules/).

Try replacing your html-loader with the following:

{
    test: /\.html$/,
    type: 'asset/source',
    generator: {
        filename: 'assets/[name]-[contenthash].[ext]',
    }
}

The generator+filename part is needed if you currently e.g. import your templates like template: require('path/to/template') and don't want to change it to template: require('path/to/template').default.

As you seem to also use scss, you will probably hit a similar issue with the processing there.

Something like the following should work for the scss processing:

{
    // Load SCSS files for Angular components
    test: /\.component\.scss$/,
    exclude: /node_modules/,
    use: [
        'to-string-loader',
        'css-loader',
        'sass-loader'
    ]
}

With this changes a component definition like the following should work fine:

@Component({
    selector: 'awesome-list',
    templateUrl: './awesome-list.component.html',
    styleUrls: ['./awesome-list.component.scss']
})
buettner123
  • 404
  • 4
  • 8
  • Thank you for your help. It helped with the template loading error. I have received some other errors and have investigated them. They do not look like being related to this issue, so I am accepting this answer. – Hiccup Test Apr 25 '23 at 09:41
  • I've just now come across this same issue. When I try what's suggested I get compilation errors. Was there anything else? – iamcootis Jul 14 '23 at 20:17
  • @iamcootis for me this was the whole solution. The compilation errors are probably caused by your specific setup. Without any more info it wont really be possible to help you – buettner123 Aug 10 '23 at 16:48