Angular 角度图表PDF字体

Angular 角度图表PDF字体,angular,amcharts,Angular,Amcharts,我正在使用Angular 7和AmCharts,并且正在导出PDF(使用附带的pdfmake库),但是我无法更改生成的PDF的默认字体。我已经用我需要的字体创建了vfs_font.js,并将其包含在scripts部分的angular.json中。在生成报告的组件中,我执行以下操作: let pdfMake = res[0]; pdfMake.fonts = { "Open Sans": { "normal": 'OpenSans-Regular.ttf',

我正在使用Angular 7和AmCharts,并且正在导出PDF(使用附带的pdfmake库),但是我无法更改生成的PDF的默认字体。我已经用我需要的字体创建了
vfs_font.js
,并将其包含在
scripts
部分的
angular.json
中。在生成报告的组件中,我执行以下操作:

let pdfMake = res[0];

pdfMake.fonts = {
    "Open Sans": {
        "normal": 'OpenSans-Regular.ttf',
        "bold": 'OpenSans-Bold.ttf',
        "italics": 'OpenSans-Italic.ttf',
        "bolditalics": 'OpenSans-BoldItalic.ttf'
    }
}

let doc = {
    header: this.drawHeader(),
    footer: this.drawFooter(),
    pageSize: "A4",
    pageOrientation: "portrait",
    pageMargins: [40, 80, 40, 60],
    content: [],
    defaultStyle: {
        font: "Open Sans"
    }
};
window.pdfMake.vfs["OpenSans-Regular.ttf"] = "BASE 64 HERE";
window.pdfMake.vfs["OpenSans-Bold.ttf"] = "BASE 64 HERE";
window.pdfMake.vfs["OpenSans-Italic.ttf"] = "BASE 64 HERE";
window.pdfMake.vfs["OpenSans-BoldItalic.ttf"] = "BASE 64 HERE";
但我得到了以下错误:

ERROR Error: Uncaught (in promise): Error: Font 'Open Sans' in style 'normal' is not defined in the font section of the document definition.
Error: Font 'Open Sans' in style 'normal' is not defined in the font section of the document definition

我怎样才能解决这个问题?感谢您

PDF制作有关如何在客户端使用自定义字体的文档


vfs_fonts.js文件格式类似于:

this.pdfMake = this.pdfMake || {}; this.pdfMake.vfs = {
  "Roboto-Italic.ttf": "AAEAAAASAQAABAAgR0RFRtRX1"
}
因此,您应该像下面这样定义它:

let pdfMake = res[0];

pdfMake.fonts = {
    "Open Sans": {
        "normal": 'OpenSans-Regular.ttf',
        "bold": 'OpenSans-Bold.ttf',
        "italics": 'OpenSans-Italic.ttf',
        "bolditalics": 'OpenSans-BoldItalic.ttf'
    }
}

let doc = {
    header: this.drawHeader(),
    footer: this.drawFooter(),
    pageSize: "A4",
    pageOrientation: "portrait",
    pageMargins: [40, 80, 40, 60],
    content: [],
    defaultStyle: {
        font: "Open Sans"
    }
};
window.pdfMake.vfs["OpenSans-Regular.ttf"] = "BASE 64 HERE";
window.pdfMake.vfs["OpenSans-Bold.ttf"] = "BASE 64 HERE";
window.pdfMake.vfs["OpenSans-Italic.ttf"] = "BASE 64 HERE";
window.pdfMake.vfs["OpenSans-BoldItalic.ttf"] = "BASE 64 HERE";
之后,您仍然需要分配pdfMake.font:

开放式SAN变体:


我在更改pdf make库的字体时也遇到了同样的问题。我使用以下步骤解决了这个问题

首先,下载所需字体的.ttf文件。

步骤1)
转到以下路径->
node\u modules->pdfmake

步骤2)
在pdfmake目录中,创建名为examples的文件夹。转到“示例”文件夹,在其中创建文件夹名称字体。

步骤3) 现在将下载的.ttf文件粘贴到fonts文件夹中。(粘贴ttf文件的路径:节点模块/pdfmake/examples/font)

一旦将文件粘贴到该文件夹中。继续执行步骤4

步骤4)
转到“终端查看”下图像“终端路径”中提到的“字体路径”文件夹以供参考。

步骤5)
一旦完成步骤4,rum
gulb buildFonts
。您的输出将如下图所示。

成功构建字体后。
转到生成pdf的组件的.ts文件。

内部.ts文件

import pdfMake from 'pdfmake/build/pdfmake';
import pdfFonts from 'pdfmake/build/vfs_fonts';
pdfMake.vfs = pdfFonts.pdfMake.vfs;


async generatePdf(value){
        pdfMake.fonts = {
            Roboto: {
                normal: 'Roboto-Regular.ttf',
                bold: 'Roboto-Medium.ttf',
                italics: 'Roboto-Italic.ttf',
                bolditalics: 'Roboto-MediumItalic.ttf'
            }
        };
        const documentDefinition = await this.getDocumentDefinition();
        pdfMake.createPdf(documentDefinition).open();
    }
    getDocumentDefinition(){
        return {
            content: (...),
              defaultStyle: {
                    font: 'yourFontName' //in this case it's roboto.
            }
        }               
    }
这解决了我的问题,如果你遇到任何困难,请告诉我。

有关更多参考信息,请按照

操作。这可能是由于
开放式SAN
中的空白造成的。尝试将其更改为
OpenSans

您可以使用amcharts使用的相同pdfmake库,但您需要使用gulp生成上述答案中提到的字体npm安装后,出现了承诺问题,因此我更换了语法,以便快速解决问题,因此,您必须转到项目根目录的node_模块中的pdfmake文件夹并运行npm安装,然后在examples目录中创建字体文件夹 ->示例/字体,并将ttf字体复制到那里,然后运行gulp文件,该文件将为您提供vfs_fonts.js文件

您需要编辑此文件并进行更改

this.pdfMake=this.pdfMake | |{};pdfMake.vfs={}

导出常量pdfMake={vfs:{};如本截图所示:

并以如下方式适当结束键:

而不是这个
}

之后,您可以在此处使用蒙特塞拉特字体查看我的完整工作代码,这是我使用下面的amchart自定义内容演示制作的:

import { Component, NgZone } from "@angular/core";
import * as am4core from "@amcharts/amcharts4/core";
import * as am4charts from "@amcharts/amcharts4/charts";
import am4themes_animated from "@amcharts/amcharts4/themes/animated";
import am4themes_dataviz from "@amcharts/amcharts4/themes/dataviz";

import * as pdfFontsVfs from "../../node_modules/pdfmake/build/vfs_fonts";
am4core.useTheme(am4themes_animated);

// Themes
am4core.useTheme(am4themes_animated);
am4core.useTheme(am4themes_dataviz);

@Component({
  selector: "app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})

export class AppComponent {
  private chart: am4charts.XYChart;
  private chart2: am4charts.XYChart;
  private chart3: am4charts.XYChart;
  private chart4: am4charts.PieChart;
  constructor(private zone: NgZone) {}

  ngAfterViewInit() {
    this.zone.runOutsideAngular(() => {

    /**
     * Chart 1
     */

    // Create chart instance
    var chart = am4core.create("chartdiv", am4charts.XYChart);

    // Add data
    chart.data = [{
      "date": new Date(2018, 0, 1),
      "value": 450,
      "value2": 362,
      "value3": 699
    }, {
      "date": new Date(2018, 0, 2),
      "value": 269,
      "value2": 450,
      "value3": 841
    }, {
      "date": new Date(2018, 0, 3),
      "value": 700,
      "value2": 358,
      "value3": 699
    }, {
      "date": new Date(2018, 0, 4),
      "value": 490,
      "value2": 367,
      "value3": 500
    }, {
      "date": new Date(2018, 0, 5),
      "value": 500,
      "value2": 485,
      "value3": 369
    }, {
      "date": new Date(2018, 0, 6),
      "value": 550,
      "value2": 354,
      "value3": 250
    }, {
      "date": new Date(2018, 0, 7),
      "value": 420,
      "value2": 350,
      "value3": 600
    }];

    // Create axes
    var categoryAxis = chart.xAxes.push(new am4charts.DateAxis());
    categoryAxis.renderer.grid.template.location = 0;
    categoryAxis.renderer.labels.template.disabled = true;
    categoryAxis.renderer.minGridDistance = 30;

    var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
    valueAxis.renderer.labels.template.disabled = true;



    this.createSeries("value", "Series #1",chart);
    this.createSeries("value2", "Series #2",chart);
    this.createSeries("value3", "Series #3",chart);
    this.chart = chart;

    /**
     * Chart 2
     */

    // Create chart instance
    var chart2 = am4core.create("chartdiv2", am4charts.XYChart);
    chart2.paddingBottom = 25;

    // Add data
    chart2.data = [{
      "country": "USA",
      "visits": 3025
    }, {
      "country": "China",
      "visits": 1882
    }, {
      "country": "Japan",
      "visits": 1809
    }, {
      "country": "Germany",
      "visits": 1322
    }, {
      "country": "UK",
      "visits": 1122
    }, {
      "country": "France",
      "visits": 1114
    }, {
      "country": "India",
      "visits": 984
    }];

    // Create axes
    var categoryAxis2 = chart2.xAxes.push(new am4charts.CategoryAxis());
    categoryAxis2.dataFields.category = "country";
    categoryAxis2.renderer.grid.template.location = 0;
    categoryAxis2.renderer.minGridDistance = 30;
    categoryAxis2.renderer.labels.template.disabled = true;

    var valueAxis = chart2.yAxes.push(new am4charts.ValueAxis());
    valueAxis.renderer.labels.template.disabled = true;

    // Create series
    var series = chart2.series.push(new am4charts.ColumnSeries());
    series.sequencedInterpolation = true;
    series.dataFields.valueY = "visits";
    series.dataFields.categoryX = "country";
    series.columns.template.strokeWidth = 0;

    series.columns.template.column.cornerRadiusTopLeft = 10;
    series.columns.template.column.cornerRadiusTopRight = 10;

    // on hover, make corner radiuses bigger
    var hoverState = series.columns.template.column.states.create("hover");
    hoverState.properties.cornerRadiusTopLeft = 0;
    hoverState.properties.cornerRadiusTopRight = 0;
    hoverState.properties.fillOpacity = 1;

    series.columns.template.adapter.add("fill", (fill, target)=>{
      return chart.colors.getIndex(target.dataItem.index);
    });
    this.chart2 = chart2;
    /**
     * Chart 3
     */

    // Create chart instance
    var chart3 = am4core.create("chartdiv3", am4charts.XYChart);
    chart3.paddingBottom = 25;

    // Add percent sign to all numbers
    chart3.numberFormatter.numberFormat = "#.3'%'";

    // Add data
    chart3.data = [{
        "country": "USA",
        "year2004": 3.5,
        "year2005": 4.2
    }, {
        "country": "UK",
        "year2004": 1.7,
        "year2005": 3.1
    }, {
        "country": "Canada",
        "year2004": 2.8,
        "year2005": 2.9
    }, {
        "country": "Japan",
        "year2004": 2.6,
        "year2005": 2.3
    }, {
        "country": "France",
        "year2004": 1.4,
        "year2005": 2.1
    }, {
        "country": "Brazil",
        "year2004": 2.6,
        "year2005": 4.9
    }];

    // Create axes
    var categoryAxis3 = chart3.xAxes.push(new am4charts.CategoryAxis());
    categoryAxis3.dataFields.category = "country";
    categoryAxis3.renderer.grid.template.location = 0;
    categoryAxis3.renderer.minGridDistance = 30;
    categoryAxis3.renderer.labels.template.disabled = true;

    var valueAxis3 = chart3.yAxes.push(new am4charts.ValueAxis());
    valueAxis3.renderer.labels.template.disabled = true;

    // Create series
    var series3 = chart3.series.push(new am4charts.ColumnSeries());
    series3.dataFields.valueY = "year2004";
    series3.dataFields.categoryX = "country";
    series3.clustered = false;

    series3.columns.template.column.cornerRadiusTopLeft = 10;
    series3.columns.template.column.cornerRadiusTopRight = 10;

    var series2 = chart3.series.push(new am4charts.ColumnSeries());
    series2.dataFields.valueY = "year2005";
    series2.dataFields.categoryX = "country";
    series2.clustered = false;
    series2.columns.template.width = am4core.percent(50);

    series2.columns.template.column.cornerRadiusTopLeft = 6;
    series2.columns.template.column.cornerRadiusTopRight = 6;
    this.chart3 = chart3;
    /**
     * Chart 4
     */

    // Create chart
    var chart4 = am4core.create("chartdiv4", am4charts.PieChart);
    chart4.padding(0, 0, 0, 0);

    chart4.data = [
      {
        country: "Lithuania",
        value: 260
      },
      {
        country: "Czech Republic",
        value: 230
      },
      {
        country: "Ireland",
        value: 200
      },
      {
        country: "Germany",
        value: 165
      },
      {
        country: "Australia",
        value: 139
      },
      {
        country: "Austria",
        value: 128
      }
    ];

    var series4 = chart4.series.push(new am4charts.PieSeries());
    series4.dataFields.value = "value";
    series4.dataFields.radiusValue = "value";
    series4.dataFields.category = "country";
    series4.slices.template.cornerRadius = 6;
    series4.colors.step = 3;
    series4.radius = am4core.percent(100);
    series4.labels.template.disabled = true;
    series4.ticks.template.disabled = true;
    this.chart4 = chart4;
    });
  }


  /**
   * Function that exports PDF
   */

  async savePDF() {

      var res = [];
      var fonts =  {
        "Montserrat": {
         normal: "Montserrat-Regular.ttf",
         bold: "Montserrat-Bold.ttf",
         italics: "Montserrat-Italic.ttf",
         bolditalics: "Montserrat-BoldItalic.ttf"
       }
     };
      var PdfMake = await this.chart.exporting.pdfmake;





      PdfMake.vfs = pdfFontsVfs.pdfMake.vfs;
      PdfMake.addVirtualFileSystem = pdfFontsVfs;
      PdfMake.fonts = fonts;

      res[1] = await this.chart.exporting.getImage("png");
      res[2] = await this.chart2.exporting.getImage("png");
      res[3] = await this.chart3.exporting.getImage("png");
      res[4] = await this.chart4.exporting.getImage("png");

      // pdfmake is ready

      // Create document template
      var doc = {
        pageSize: "A4",
        pageOrientation: "portrait",
        pageMargins: [30, 30, 30, 30],
        content: [],
        defaultStyle:{
          font: "Montserrat"
        }
      };

      doc.content.push({
        text: "In accumsan velit in orci tempor",
        fontSize: 20,
        bold: true,
        margin: [0, 20, 0, 15]
      });

      doc.content.push({
        text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sem quam, sodales ac volutpat sed, vestibulum id quam. Sed quis arcu non elit fringilla mattis. Sed auctor mi sed efficitur vehicula. Sed bibendum odio urna, quis lobortis dui luctus ac. Duis eu lacus sodales arcu tincidunt ultrices viverra a risus. Vivamus justo massa, malesuada quis pellentesque ut, placerat in massa. Nunc bibendum diam justo, in consequat ipsum fringilla ac. Praesent porta nibh ac arcu viverra, at scelerisque neque venenatis. Donec aliquam lorem non ultrices ultrices. Aliquam efficitur eros quis tortor condimentum, id pellentesque metus iaculis. Aenean at consequat neque, a posuere lectus. In eu libero magna. Pellentesque molestie tellus nec nisi molestie, eu dignissim lacus tristique. Sed tellus nulla, suscipit a velit non, mattis dictum metus. Curabitur mi mi, convallis nec libero quis, venenatis vestibulum ante.",
        fontSize: 15,
        margin: [0, 0, 0, 15]
      });

      doc.content.push({
        text: "Aliquam lacinia justo",
        fontSize: 20,
        bold: true,
        margin: [0, 20, 0, 15]
      });

      doc.content.push({
        image: res[1],
        width: 530
      });

      doc.content.push({
        text: "Phasellus suscipit in diam a interdum",
        fontSize: 20,
        bold: true,
        margin: [0, 20, 0, 15]
      });

      doc.content.push({
        table: {
          headerRows: 1,
          widths: [ "*", "*", "*", "*" ],
          body: [
            [
              { text: "USA", bold: true },
              { text: "Japan", bold: true },
              { text: "France", bold: true },
              { text: "Mexico", bold: true }
            ],
            [ "2500", "2500", "2200", "1200" ],
            [ "800", "1200", "990", "708" ],
            [ "2100", "2150", "900", "1260" ],
          ]
        }
      });

      doc.content.push({
        text: "Duis sed efficitur mauris",
        fontSize: 20,
        bold: true,
        margin: [0, 20, 0, 15]
      });

      doc.content.push({
        columns: [{
          image: res[2],
          width: 250
        }, {
          image: res[3],
          width: 250
        }],
        columnGap: 30
      });

      doc.content.push({
        text: "Aliquam semper lacinia",
        fontSize: 20,
        bold: true,
        margin: [0, 20, 0, 15]
      });

      doc.content.push({
        columns: [{
          image: res[4],
          width: 150
        }, {
          stack: [{
            text: "Maecenas congue leo vel tortor faucibus, non semper odio viverra. In ac libero rutrum libero elementum blandit vel in orci. Donec sit amet nisl ac eros mollis molestie. Curabitur ut urna vitae turpis bibendum malesuada sit amet imperdiet orci. Etiam pulvinar quam at lorem pellentesque congue. Integer sed odio enim. Maecenas eu nulla justo. Sed quis enim in est sodales facilisis non sed erat. Aenean vel ornare urna. Praesent viverra volutpat ex a aliquet.",
            fontSize: 15,
            margin: [0, 0, 0, 15]
          }, {
       text: "Fusce sed quam pharetra, ornare ligula id, maximus risus. Integer dignissim risus in placerat mattis. Fusce malesuada dui ut lectus ultricies, et sollicitudin nisl placerat. In dignissim elit in pretium lobortis. Fusce ornare enim at metus laoreet, ut convallis elit lacinia. Maecenas pharetra aliquet mi. Nulla orci nunc, egestas id nisi ut, volutpat sollicitudin mi.",
        fontSize: 15,
        margin: [0, 0, 0, 15]
      }],
      width: "*"
    }],
    columnGap: 30
  });

    await  PdfMake.createPdf(doc,null, fonts,pdfFontsVfs.pdfMake.vfs).download("report.pdf");


  }

  // Create series
  createSeries(field, name, chart) {
    console.log(chart);
    var series = chart.series.push(new am4charts.LineSeries());
    series.dataFields.valueY = field;
    series.dataFields.dateX = "date";
    series.name = name;
    series.tooltipText = "{dateX}: [b]{valueY}[/]";
    series.strokeWidth = 3;

    var bullet = series.bullets.push(new am4charts.CircleBullet());
    bullet.circle.stroke = am4core.color("#fff");
    bullet.circle.strokeWidth = 3;
    bullet.circle.radius = 7;
  }
  ngOnDestroy() {
    this.zone.runOutsideAngular(() => {
      if (this.chart) {
        this.chart.dispose();
        ...dispose other charts
      }
    });
  }
}
网页包配置为:

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: './src/main.ts',
    resolve: {
        extensions: ['.ts', '.js']
    },
    module: {
        rules: [
            {
                test: /\.ts$/,
                use: ['ts-loader', 'angular2-template-loader'],
                exclude: [ /node_modules/, /pdfmake.js$/ ]
            },
            {
                test: /\.(html|css)$/,
                use: 'raw-loader'
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({ template: './src/index.html' })
    ],

}
html是:

    <div class="main">

  <input type="button" value="Save as PDF" (click)="savePDF()" />

<h1>In accumsan velit in orci tempor</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sem quam, sodales ac volutpat sed, vestibulum id quam. Sed quis arcu non elit fringilla mattis. Sed auctor mi sed efficitur vehicula. Sed bibendum odio urna, quis lobortis dui luctus ac. Duis eu lacus sodales arcu tincidunt ultrices viverra a risus. Vivamus justo massa, malesuada quis pellentesque ut, placerat in massa. Nunc bibendum diam justo, in consequat ipsum fringilla ac. Praesent porta nibh ac arcu viverra, at scelerisque neque venenatis. Donec aliquam lorem non ultrices ultrices. Aliquam efficitur eros quis tortor condimentum, id pellentesque metus iaculis. Aenean at consequat neque, a posuere lectus. In eu libero magna. Pellentesque molestie tellus nec nisi molestie, eu dignissim lacus tristique. Sed tellus nulla, suscipit a velit non, mattis dictum metus. Curabitur mi mi, convallis nec libero quis, venenatis vestibulum ante.</p>
<h2>Aliquam lacinia justo</h2>
<div id="chartdiv" class="chart"></div>

<h2>Phasellus suscipit in diam a interdum</h2>
<table>
  <tr>
    <th>USA</th>
    <th>Japan</th>
    <th>France</th>
    <th>Mexico</th>
  </tr>
  <tr>
    <td>2500</td>
    <td>1900</td>
    <td>2200</td>
    <td>1200</td>
  </tr>
  <tr>
    <td>800</td>
    <td>1200</td>
    <td>990</td>
    <td>708</td>
  </tr>
  <tr>
    <td>2100</td>
    <td>2150</td>
    <td>900</td>
    <td>1260</td>
  </tr>
</table>

<h2>Duis sed efficitur mauris</h2>
<div>
  <div class="col">
    <div id="chartdiv2" class="chart"></div>
  </div>
  <div class="col">
    <div id="chartdiv3" class="chart"></div>
  </div>
</div>

<br>
<h2>Aliquam semper lacinia</h2>
<div id="chartdiv4" class="chart"></div>
<p>Maecenas congue leo vel tortor faucibus, non semper odio viverra. In ac libero rutrum libero elementum blandit vel in orci. Donec sit amet nisl ac eros mollis molestie. Curabitur ut urna vitae turpis bibendum malesuada sit amet imperdiet orci. Etiam pulvinar quam at lorem pellentesque congue. Integer sed odio enim. Maecenas eu nulla justo. Sed quis enim in est sodales facilisis non sed erat. Aenean vel ornare urna. Praesent viverra volutpat ex a aliquet.</p>

<p>Fusce sed quam pharetra, ornare ligula id, maximus risus. Integer dignissim risus in placerat mattis. Fusce malesuada dui ut lectus ultricies, et sollicitudin nisl placerat. In dignissim elit in pretium lobortis. Fusce ornare enim at metus laoreet, ut convallis elit lacinia. Maecenas pharetra aliquet mi. Nulla orci nunc, egestas id nisi ut, volutpat sollicitudin mi.</p>

</div>
和文件夹结构:

我理解的是语法冲突和承诺未解决的故障,阻止图表pdf不加载自定义字体。如果您看到默认的amcharts vs_fonts文件,您将看到可能导致问题的导出默认语法。其次,检查字体文件是否损坏。
希望这对你有帮助。谢谢。

试试看,我有预感。您可以尝试删除“开放式SAN”、“普通”、“粗体”、“斜体”和“粗体斜体”中的双引号吗?如果出现错误,请尝试删除Open和SAN之间的空间,使其成为OpenSAN。顺便说一句,这个问题已经被报道了,但没有解决方案:即使在删除quotesPlease放置vfs_fonts.js文件内容后仍然存在同样的问题。同样,我没有使用独立的pdfmake库,我使用的是AMCharts库中的pdfmake库,所以我不能这样做,但毕竟你关心的是pdfmake库字体。那你为什么不用它呢?
    {
    "name": "angular-7-tutorial",
    "version": "1.0.0",
    "scripts": {
        "start": "webpack-dev-server --mode development --open"
    },
    "dependencies": {
        "@amcharts/amcharts4": "^4.9.1",
        "@angular/common": "^7.2.13",
        "@angular/compiler": "^7.2.13",
        "@angular/core": "^7.2.13",
        "@angular/forms": "^7.2.13",
        "@angular/platform-browser": "^7.2.13",
        "@angular/platform-browser-dynamic": "^7.2.13",
        "@angular/router": "^7.2.13",
        "core-js": "^3.0.1",
        "rxjs": "^6.4.0",
        "zone.js": "^0.9.0"
    },
    "devDependencies": {
        "@types/node": "^11.13.5",
        "angular2-template-loader": "^0.6.2",
        "html-webpack-plugin": "^3.2.0",
        "raw-loader": "^1.0.0",
        "ts-loader": "^5.3.3",
        "typescript": "^3.4.4",
        "webpack": "^4.30.0",
        "webpack-cli": "^3.3.0",
        "webpack-dev-server": "^3.3.1"
    }
}