Saturday, March 31, 2018

Including jquery-sparkline with webpack

Leave a Comment

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

1 Answers

Answers 1

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

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment