I'm struggling with my first attempt with webpack.
I'm getting the following error in the browser console.
ERROR TypeError: $(...).sparkline is not a function
This is my webpack.config.vendor.js code
const path = require('path'); const webpack = require('webpack'); //const ExtractTextPlugin = require('extract-text-webpack-plugin'); const merge = require('webpack-merge'); const treeShakableModules = [ '@angular/animations', '@angular/common', '@angular/compiler', '@angular/core', '@angular/forms', '@angular/http', '@angular/platform-browser', '@angular/platform-browser-dynamic', '@angular/router', 'zone.js/dist/zone', ]; const nonTreeShakableModules = [ 'jquery', 'jquery-sparkline', '.\\node_modules\\jquery-sparkline\\jquery.sparkline.js', '@angular/material', 'event-source-polyfill', '.\\wwwroot\\assets\\styles\\style.scss', '.\\node_modules\\chartist\\dist\\chartist.css', '.\\node_modules\\quill\\dist\\quill.snow.css', '.\\node_modules\\quill\\dist\\quill.bubble.css', '.\\node_modules\\angular-calendar\\css\\angular-calendar.css', '.\\node_modules\\dragula\\dist\\dragula.css', '.\\ClientApp\\styles.css', ]; const allModules = treeShakableModules.concat(nonTreeShakableModules); module.exports = (env) => { const isDevBuild = !(env && env.prod); const sharedConfig = { stats: { "modules": true }, resolve: { extensions: ['.js'], }, module: { rules: [ { test: /\.(png|woff|woff2|eot|ttf|svg)(\?|$)/, use: 'url-loader?limit=100000' } ] }, output: { publicPath: 'dist/', filename: '[name].js', library: '[name]_[hash]' }, plugins: [ new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' }), // Maps these identifiers to the jQuery package (because Bootstrap expects it to be a global variable) new webpack.ContextReplacementPlugin(/\@angular\b.*\b(bundles|linker)/, path.join(__dirname, './ClientApp')), // Workaround for https://github.com/angular/angular/issues/11580 new webpack.ContextReplacementPlugin(/angular(\\|\/)core(\\|\/)@angular/, path.join(__dirname, './ClientApp')), // Workaround for https://github.com/angular/angular/issues/14898 new webpack.ContextReplacementPlugin(/\@angular(\\|\/)core(\\|\/)esm5/, path.join(__dirname, './ClientApp')), // Workaround for https://github.com/angular/angular/issues/20357 new webpack.IgnorePlugin(/^vertx$/) // Workaround for https://github.com/stefanpenner/es6-promise/issues/100 ] }; const clientBundleConfig = merge(sharedConfig, { entry: { // To keep development builds fast, include all vendor dependencies in the vendor bundle. // But for production builds, leave the tree-shakable ones out so the AOT compiler can produce a smaller bundle. vendor: isDevBuild ? allModules : nonTreeShakableModules }, output: { path: path.join(__dirname, 'wwwroot', 'dist') }, module: { rules: [ { test: /\.scss$/, use: ['to-string-loader', 'css-loader', 'sass-loader'] }, { test: /\.css$/, use: ['to-string-loader', isDevBuild ? 'css-loader' : 'css-loader?minimize'] }, //{ //test: /\.scss$/, use: ExtractTextPlugin.extract({ // use: ['css-loader', 'sass-loader'], // // use style-loader in development // fallback: "style-loader" //}) //} ] }, plugins: [ new webpack.DllPlugin({ context: __dirname, path: path.join(__dirname, 'wwwroot', 'dist', '[name]-manifest.json'), name: '[name]_[hash]' }) ].concat(isDevBuild ? [] : [ new webpack.optimize.UglifyJsPlugin() ]) }); const serverBundleConfig = merge(sharedConfig, { target: 'node', resolve: { mainFields: ['main'] }, entry: { vendor: allModules.concat(['aspnet-prerendering']) }, output: { path: path.join(__dirname, 'ClientApp', 'dist'), libraryTarget: 'commonjs2', }, module: { rules: [ { test: /\.scss$/, use: ['to-string-loader', 'css-loader', 'sass-loader'] }, { test: /\.css$/, use: ['to-string-loader', isDevBuild ? 'css-loader' : 'css-loader?minimize'] }, //{ //test: /\.scss$/, use: ExtractTextPlugin.extract({ // use: ['css-loader', 'sass-loader'], // // use style-loader in development // fallback: "style-loader" //}) //} ] }, plugins: [ new webpack.DllPlugin({ context: __dirname, path: path.join(__dirname, 'ClientApp', 'dist', '[name]-manifest.json'), name: '[name]_[hash]' }) ] }); return [clientBundleConfig, serverBundleConfig]; }
And this is my webpack.config.js code.
const path = require('path'); const webpack = require('webpack'); const merge = require('webpack-merge'); const AngularCompilerPlugin = require('@ngtools/webpack').AngularCompilerPlugin; const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin; const ExtractTextPlugin = require('extract-text-webpack-plugin'); module.exports = (env) => { // Configuration in common to both client-side and server-side bundles const isDevBuild = !(env && env.prod); const sharedConfig = { stats: { modules: true }, context: __dirname, resolve: { extensions: ['.js', '.ts'] }, output: { filename: '[name].js', publicPath: 'dist/' // Webpack dev middleware, if enabled, handles requests for this URL prefix }, module: { rules: [ { test: /\.ts$/, use: isDevBuild ? ['awesome-typescript-loader?silent=true', 'angular2-template-loader', 'angular2-router-loader'] : '@ngtools/webpack' }, { test: /\.html$/, use: 'html-loader?minimize=false' }, { test: /\.css$/, use: ['to-string-loader', isDevBuild ? 'css-loader' : 'css-loader?minimize'] }, //{ // test: /\.scss$/, use: ExtractTextPlugin.extract({ // use: ['css-loader', 'sass-loader'], // // use style-loader in development // fallback: "style-loader" // }) //}, { test: /\.scss$/, use: ['to-string-loader', 'css-loader', 'sass-loader'] }, { test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader?limit=25000' } ] }, plugins: [ new ExtractTextPlugin({ filename: 'vendor.css', disable: false, allChunks: true }), new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' }), // Maps these identifiers to the jQuery package (because Bootstrap expects it to be a global variable) new CheckerPlugin() ] }; // Configuration for client-side bundle suitable for running in browsers const clientBundleOutputDir = './wwwroot/dist'; const clientBundleConfig = merge(sharedConfig, { entry: { 'main-client': './ClientApp/boot.browser.ts' }, output: { path: path.join(__dirname, clientBundleOutputDir) }, plugins: [ new webpack.DllReferencePlugin({ context: __dirname, manifest: require('./wwwroot/dist/vendor-manifest.json') }) ].concat(isDevBuild ? [ // Plugins that apply in development builds only new webpack.SourceMapDevToolPlugin({ filename: '[file].map', // Remove this line if you prefer inline source maps moduleFilenameTemplate: path.relative(clientBundleOutputDir, '[resourcePath]') // Point sourcemap entries to the original file locations on disk }) ] : [ // Plugins that apply in production builds only new webpack.optimize.UglifyJsPlugin(), new AngularCompilerPlugin({ tsConfigPath: './tsconfig.json', entryModule: path.join(__dirname, 'ClientApp/app/components/app.browser.module#AppModule'), exclude: ['./**/*.server.ts'] }) ]) }); // Configuration for server-side (prerendering) bundle suitable for running in Node const serverBundleConfig = merge(sharedConfig, { resolve: { mainFields: ['main'] }, entry: { 'main-server': './ClientApp/boot.server.ts' }, plugins: [ new webpack.DllReferencePlugin({ context: __dirname, manifest: require('./ClientApp/dist/vendor-manifest.json'), sourceType: 'commonjs2', name: './vendor' }) ].concat(isDevBuild ? [] : [ // Plugins that apply in production builds only new AngularCompilerPlugin({ tsConfigPath: './tsconfig.json', entryModule: path.join(__dirname, 'ClientApp/app/app.server.module#AppModule'), exclude: ['./**/*.browser.ts'] }) ]), output: { libraryTarget: 'commonjs', path: path.join(__dirname, './ClientApp/dist') }, target: 'node', devtool: 'inline-source-map' }); return [clientBundleConfig, serverBundleConfig]; };
I've included jquery-sparkline but I still get that error. I can see the sparkline code inside vendor.js but it doesn't appear to make any difference.
I also looked at the vendor manifest file and it contains this but no other mention of sparkline.
"./node_modules/jquery-sparkline/jquery.sparkline.js":{ "id":101, "meta":{ } }
I also don't understand why I had to put the ProvidePlugin below in both files. Surely once should be enough, but when it was only in the vendor file, I got browser errors saying that it couldn't find $.
new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' })
Any help would be greatly appreciated!
Thanks
It's look like you are using angular-netcore.
This case, you need install types
for sparkline, since not have ready yet, you have to create one.
ClientApp/typings/sparkline/index.d.ts
index.d.ts
// Generated by typings // Source: ClientApp/typings/sparkline/index.d.ts interface JQuery { sparkline(values?: string | Array<(string | number)>, opts?: sparkline.Settings): any; } declare namespace sparkline { interface Settings { type?: string; barColor?: string; width?: string | number; height?: string; lineColor?: string; fillColor?: string | number; chartRangeMin?: string | number; chartRangeMax?: string | number; composite?: boolean; enableTagOptions?: boolean; tagOptionPrefix?: string; tagValuesAttribute?: string; disableHiddenCheck?: boolean; } }
next you need declare to global:
typings install --global --save file:./ClientApp/typings/sparkline/index.d.ts
if you don't have typings
, you can install by
yarn install typings
and last add jquery-sparkline
to boot.browser.ts
boot.browser.ts
import 'reflect-metadata'; import 'zone.js'; import 'bootstrap'; import 'jquery-sparkline'; import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.browser.module'; ...
because boot.browser.ts
is configuration for client-side bundle suitable for running in browsers.
!!!IMPORTANT
Don't put import 'jquery-sparkline';
to app/component because we don't want inject to boot.server
as well (will throw error cause Server-side prerendering).
Need full code example here: dotnet-core-angular-sparkline