build(aio): ability to generate plunkers

This commit is contained in:
Jesus Rodriguez 2017-03-28 19:29:47 +02:00 committed by Hans
parent f5a98d98c8
commit 3ced940b5a
10 changed files with 693 additions and 5 deletions

View File

@ -24,7 +24,8 @@
"pre~~deploy": "yarn build",
"~~deploy": "firebase deploy --message \"Commit: $TRAVIS_COMMIT\" --non-interactive --token \"$FIREBASE_TOKEN\"",
"boilerplate:add": "node ./tools/examples/add-example-boilerplate add",
"boilerplate:remove": "node ./tools/examples/add-example-boilerplate remove"
"boilerplate:remove": "node ./tools/examples/add-example-boilerplate remove",
"generate-plunkers": "node ./tools/plunker-builder/generatePlunkers"
},
"private": true,
"dependencies": {
@ -62,6 +63,7 @@
"html": "^1.0.0",
"jasmine-core": "~2.5.2",
"jasmine-spec-reporter": "~3.2.0",
"jsdom": "^9.12.0",
"karma": "~1.4.1",
"karma-chrome-launcher": "~2.0.0",
"karma-cli": "~1.0.1",

View File

@ -12,6 +12,7 @@ const EXAMPLES_TESTING_PATH = path.join(EXAMPLES_PATH, 'testing');
const files = {
exampleBoilerplate: [
'src/styles.css',
'src/systemjs-angular-loader.js',
'src/systemjs.config.js',
'src/tsconfig.json',
'bs-config.json',

View File

@ -0,0 +1,309 @@
'use strict';
// Canonical path provides a consistent path (i.e. always forward slashes) across different OSes
var path = require('canonical-path');
var Q = require('q');
var _ = require('lodash');
var jsdom = require("jsdom");
var fs = require("fs-extra");
var globby = require('globby');
var fileTranslator = require('./translator/fileTranslator');
var indexHtmlRules = require('./translator/rules/indexHtml');
var systemjsConfigExtrasRules = require('./translator/rules/systemjsConfigExtras');
var regionExtractor = require('../../transforms/examples-package/services/region-parser');
class PlunkerBuilder {
constructor(basePath, destPath, options) {
this.basePath = basePath;
this.destPath = destPath;
this.options = options;
this.boilerplate = path.join(__dirname, '../examples/shared/boilerplate');
this.copyrights = {};
this._buildCopyrightStrings();
}
buildPlunkers() {
this._getPlunkerFiles();
var errFn = this.options.errFn || function(e) { console.log(e); };
var plunkerPaths = path.join(this.basePath, '**/*plnkr.json');
var fileNames = globby.sync(plunkerPaths, { ignore: ['**/node_modules/**'] });
fileNames.forEach((configFileName) => {
try {
this._buildPlunkerFrom(configFileName);
} catch (e) {
errFn(e);
}
});
}
_addPlunkerFiles(postData) {
this.options.addField(postData, 'systemjs.config.js', this.systemjsConfig);
this.options.addField(postData, 'systemjs-angular-loader.js', this.systemjsModulePlugin);
}
_buildCopyrightStrings() {
var copyright = 'Copyright 2017 Google Inc. All Rights Reserved.\n'
+ 'Use of this source code is governed by an MIT-style license that\n'
+ 'can be found in the LICENSE file at http://angular.io/license';
var pad = '\n\n';
this.copyrights.jsCss = `${pad}/*\n${copyright}\n*/`;
this.copyrights.html = `${pad}<!-- \n${copyright}\n-->`;
}
// Build plunker from JSON configuration file (e.g., plnkr.json):
// all properties are optional
// files: string[] - array of globs - defaults to all js, ts, html, json, css and md files (with certain files removed)
// description: string - description of this plunker - defaults to the title in the index.html page.
// tags: string[] - optional array of plunker tags (for searchability)
// main: string - name of file that will become index.html in the plunker - defaults to index.html
// open: string - name of file to display within the plunker as in "open": "app/app.module.ts"
_buildPlunkerFrom(configFileName) {
// replace ending 'plnkr.json' with 'plnkr.no-link.html' to create output file name;
var outputFileName = `${this.options.plunkerFileName}.no-link.html`;
outputFileName = configFileName.replace(/plnkr\.json$/, outputFileName);
var altFileName;
if (this.destPath && this.destPath.length > 0) {
var partPath = path.dirname(path.relative(this.basePath, outputFileName));
var altFileName = path.join(this.destPath, partPath, path.basename(outputFileName)).replace('.no-link.', '.');
}
try {
var config = this._initConfigAndCollectFileNames(configFileName);
var postData = this._createPostData(config);
this._addPlunkerFiles(postData);
var html = this._createPlunkerHtml(config, postData);
if (this.options.writeNoLink) {
fs.writeFileSync(outputFileName, html, 'utf-8');
}
if (altFileName) {
var altDirName = path.dirname(altFileName);
fs.ensureDirSync(altDirName);
fs.writeFileSync(altFileName, html, 'utf-8');
}
} catch (e) {
// if we fail delete the outputFile if it exists because it is an old one.
if (this._existsSync(outputFileName)) {
fs.unlinkSync(outputFileName);
}
if (altFileName && this._existsSync(altFileName)) {
fs.unlinkSync(altFileName);
}
throw e;
}
}
_createBasePlunkerHtml(config, embedded) {
var open = '';
if (config.open) {
open = embedded ? `&show=${config.open}` : `&open=${config.open}`;
}
var action = `${this.options.url}${open}`;
var html = '<!DOCTYPE html><html lang="en"><body>';
html += `<form id="mainForm" method="post" action="${action}" target="_self">`;
// html += '<div class="button"><button id="formButton" type="submit">Create Plunker</button></div>'
// html += '</form><script>document.getElementById("formButton").click();</script>'
html += '</form><script>document.getElementById("mainForm").submit();</script>';
html += '</body></html>';
return html;
}
_createPostData(config) {
var postData = {};
config.fileNames.forEach((fileName) => {
var content;
var extn = path.extname(fileName);
if (extn == '.png') {
content = this._encodeBase64(fileName);
fileName = fileName.substr(0, fileName.length - 4) + '.base64.png'
} else if (-1 < fileName.indexOf('systemjs.config.extras')) {
content = this._getSystemjsConfigExtras(config);
} else {
content = fs.readFileSync(fileName, 'utf-8');
}
if (extn == '.js' || extn == '.ts' || extn == '.css') {
content = content + this.copyrights.jsCss;
} else if (extn == '.html') {
content = content + this.copyrights.html;
}
// var escapedValue = escapeHtml(content);
var relativeFileName = path.relative(config.basePath, fileName);
if (relativeFileName == config.main) {
relativeFileName = 'index.html';
}
if (relativeFileName == 'index.html') {
content = fileTranslator.translate(content, indexHtmlRules);
if (config.description == null) {
// set config.description to title from index.html
var matches = /<title>(.*)<\/title>/.exec(content);
if (matches) {
config.description = matches[1];
}
}
}
if (relativeFileName == 'systemjs.config.extras.js') {
content = fileTranslator.translate(content, systemjsConfigExtrasRules);
}
content = regionExtractor()(content, extn.substr(1)).contents;
this.options.addField(postData, relativeFileName, content);
});
var tags = ['angular', 'example'].concat(config.tags || []);
tags.forEach(function(tag,ix) {
postData['tags[' + ix + ']'] = tag;
});
if (!this.options.embedded) {
postData.private = true;
postData.description = "Angular Example - " + config.description;
} else {
postData.title = "Angular Example - " + config.description;
}
// Embedded needs to add more content, so if the callback is available, we call it
if (this.options.extraData) {
this.options.extraData(postData, config);
}
return postData;
}
_createPlunkerHtml(config, postData) {
var baseHtml = this._createBasePlunkerHtml(config, this.options.embedded);
var doc = jsdom.jsdom(baseHtml);
var form = doc.querySelector('form');
_.forEach(postData, (value, key) => {
var ele = this._htmlToElement(doc, '<input type="hidden" name="' + key + '">');
ele.setAttribute('value', value);
form.appendChild(ele)
});
var html = doc.documentElement.outerHTML;
return html;
}
_encodeBase64(file) {
// read binary data
var bitmap = fs.readFileSync(file);
// convert binary data to base64 encoded string
return Buffer(bitmap).toString('base64');
}
_existsSync(filename) {
try {
fs.accessSync(filename);
return true;
} catch(ex) {
return false;
}
}
_getPlunkerFiles() {
var systemJsModulePlugin = '/src/systemjs-angular-loader.js';
var systemJsConfigPath = '/src/systemjs.config.web.js';
if (this.options.build) {
systemJsConfigPath = '/src/systemjs.config.web.build.js';
}
this.systemjsConfig = fs.readFileSync(this.boilerplate + systemJsConfigPath, 'utf-8');
this.systemjsModulePlugin = fs.readFileSync(this.boilerplate + systemJsModulePlugin, 'utf-8');
// Copyright already added to web versions of systemjs.config
// this.systemjsConfig += this.copyrights.jsCss;
}
// Try to replace `systemjs.config.extras.js` with the
// `systemjs.config.extras.web.js` web version that
// should default SystemJS barrels to `.ts` files rather than `.js` files
// Example: see docs `testing`.
// HACK-O-MATIC!
_getSystemjsConfigExtras(config) {
var extras = config.basePath + '/systemjs.config.extras.js';
var webExtras = config.basePath + '/systemjs.config.extras.web.js';
if (this._existsSync(webExtras)) {
// console.log('** Substituted "' + webExtras + '" for "' + extras + '".');
return fs.readFileSync(webExtras, 'utf-8');
} else if (this._existsSync(extras)){
console.log('** WARNING: no "' + webExtras + '" replacement for "' + extras + '".');
return fs.readFileSync(extras, 'utf-8');
} else {
console.log('** WARNING: no "' + extras + '" file; returning empty content.');
return '';
}
}
_htmlToElement(document, html) {
var div = document.createElement('div');
div.innerHTML = html;
return div.firstChild;
}
_initConfigAndCollectFileNames(configFileName) {
var configDir = path.dirname(configFileName);
var configSrc = fs.readFileSync(configFileName, 'utf-8');
try {
var config = (configSrc && configSrc.trim().length) ? JSON.parse(configSrc) : {};
config.basePath = config.basePath ? path.resolve(configDir, config.basePath) : configDir;
} catch (e) {
throw new Error(`Plunker config - unable to parse json file: ${configFileName}\n${e}`);
}
var defaultIncludes = ['**/*.ts', '**/*.js', '**/*.css', '**/*.html', '**/*.md', '**/*.json', '**/*.png'];
if (config.files) {
if (config.files.length > 0) {
if (config.files[0].substr(0, 1) == '!') {
config.files = defaultIncludes.concat(config.files);
}
}
} else {
config.files = defaultIncludes;
}
var includeSpec = false;
var gpaths = config.files.map(function(fileName) {
fileName = fileName.trim();
if (fileName.substr(0,1) == '!') {
return "!" + path.join(config.basePath, fileName.substr(1));
} else {
includeSpec = includeSpec || /\.spec\.(ts|js)$/.test(fileName);
return path.join(config.basePath, fileName);
}
});
var defaultExcludes = [
'!**/tsconfig.json',
'!**/*plnkr.*',
'!**/package.json',
'!**/example-config.json',
'!**/tslint.json',
'!**/.editorconfig',
'!**/systemjs.config.js',
'!**/wallaby.js',
'!**/karma-test-shim.js',
'!**/karma.conf.js',
// AoT related files
'!**/aot/**/*.*',
'!**/*-aot.*'
];
// exclude all specs if no spec is mentioned in `files[]`
if (!includeSpec) {
defaultExcludes.push('!**/*.spec.*','!**/spec.js');
}
gpaths.push(...defaultExcludes);
config.fileNames = globby.sync(gpaths, { ignore: ["**/node_modules/**"] });
return config;
}
}
module.exports = PlunkerBuilder;

View File

@ -0,0 +1,34 @@
var PlunkerBuilder = require('./builder');
function buildPlunkers(basePath, destPath, options = {}) {
configureBuilder(options);
var builder = new PlunkerBuilder(basePath, destPath, options);
builder.buildPlunkers();
}
function configureBuilder(options) {
options.addField = addField;
options.plunkerFileName = 'eplnkr';
options.url = 'https://embed.plnkr.co?show=preview';
options.writeNoLink = false;
options.embedded = true;
options.extraData = extraData;
}
function extraData(postData, config) {
postData['source[type]'] = config.description || 'Angular example';
postData['source[url]'] = 'https://angular.io'
}
function addField(postData, name, content) {
var encoding = 'utf8';
if (name.split('.').pop() === 'png') {
encoding = 'base64';
}
postData[`entries[${name}][content]`] = content;
postData[`entries[${name}][encoding]`] = encoding;
}
module.exports = {
buildPlunkers: buildPlunkers
};

View File

@ -0,0 +1,9 @@
const path = require('path');
const regularPlunker = require('./regularPlunker');
const embeddedPlunker = require('./embeddedPlunker');
const EXAMPLES_PATH = path.join(__dirname, '../../content/examples');
const LIVE_EXAMPLES_PATH = path.join(__dirname, '../../src/content/live-examples');
regularPlunker.buildPlunkers(EXAMPLES_PATH, LIVE_EXAMPLES_PATH);
embeddedPlunker.buildPlunkers(EXAMPLES_PATH, LIVE_EXAMPLES_PATH);

View File

@ -0,0 +1,23 @@
var PlunkerBuilder = require('./builder');
function buildPlunkers(basePath, destPath, options = {}) {
configureBuilder(options);
var builder = new PlunkerBuilder(basePath, destPath, options);
builder.buildPlunkers();
}
function configureBuilder(options) {
options.addField = addField;
options.plunkerFileName = 'plnkr';
options.url = 'http://plnkr.co/edit/?p=preview';
options.writeNoLink = true;
options.embedded = false;
}
function addField(postData, name, content) {
postData[`files[${name}]`] = content;
}
module.exports = {
buildPlunkers: buildPlunkers
};

View File

@ -0,0 +1,47 @@
// var first_time = true; // DIAGNOSTIC
function translate(html, rulesFile) {
rulesFile.rulesToApply.forEach(function(rxDatum) {
var rxRule = rulesFile.rules[rxDatum.pattern];
// rxFrom is a rexexp
var rxFrom = rxRule.from;
if (rxDatum.from) {
var from = rxDatum.from.replace('/', '\/');
var rxTemp = rxFrom.toString();
rxTemp = rxTemp.replace('%tag%', from);
rxFrom = rxFromString(rxTemp);
}
// rxTo is a string
var rxTo = rxRule.to;
if (rxDatum.to) {
var to = rxDatum.to;
to = Array.isArray(to) ? to : [to];
to = to.map(function (toItem) {
return rxTo.replace("%tag%", toItem);
});
rxTo = to.join("\n ");
}
/* DIAGNOSTIC
if (first_time && rxDatum.pattern === 'zone_pkg') {
first_time = false;
console.log('zone_pkg');
console.log(' rxFrom: '+rxFrom);
console.log(' rxTo: '+rxTo);
console.log(' replace: ' + html.replace(rxFrom, rxTo ));
}
*/
html = html.replace(rxFrom, rxTo);
});
return html;
}
function rxFromString(rxString) {
var rx = /^\/(.*)\/(.*)/;
var pieces = rx.exec(rxString);
return RegExp(pieces[1], pieces[2]);
}
module.exports = {translate: translate};

View File

@ -0,0 +1,115 @@
var rules = {
basehref: {
from: /<base href=".*"[/]?>/,
to: '<script>document.write(\'<base href="\' + document.location + \'" />\');</script>'
},
angular_pkg: {
from: /src=".?node_modules\/@angular/g,
to: 'src="https://unpkg.com/@angular'
},
script: {
from: /<script.*".*%tag%".*>.*<\/script>/,
to: '<script src="%tag%"></script>'
},
link: {
from: '/<link rel="stylesheet" href=".*%tag%".*>/',
to: '<link rel="stylesheet" href="%tag%">'
},
// Clear script like this:
// <script>
// System.import('app').catch(function(err){ console.error(err); });
// </script>
system_strip_import_app: {
from: /<script>[^]?\s*System.import\('app'\)[^]*\/script>/,
to: ''
},
system_extra_main: {
from: /main:\s*[\'|\"]index.js[\'|\"]/,
to: 'main: "index.ts"'
},
system_extra_defaultExtension: {
from: /defaultExtension:\s*[\'|\"]js[\'|\"]/,
to: 'defaultExtension: "ts"'
},
zone_pkg: {
from: /src=".?node_modules\/zone.js\/dist\/(.*)"/g,
to: 'src="https://unpkg.com/zone.js/dist/$1?main=browser"'
},
};
var rulesToApply = [
{
pattern: 'basehref',
},
{
pattern: 'script',
from: 'node_modules/core-js/client/shim.min.js',
to: 'https://unpkg.com/core-js/client/shim.min.js'
},
{
pattern: 'script',
from: 'node_modules/zone.js/dist/zone.js',
to: 'https://unpkg.com/zone.js@0.7.4?main=browser'
},
{
pattern: 'script',
from: 'node_modules/rxjs/bundles/Rx.js',
to: 'https://unpkg.com/rxjs@5.0.1/bundles/Rx.js'
},
{
pattern: 'script',
from: 'node_modules/systemjs/dist/system.src.js',
to: 'https://unpkg.com/systemjs@0.19.39/dist/system.src.js'
},
{
pattern: 'script',
from: 'node_modules/angular/in-memory-web-api/web-api.js',
to: 'https://unpkg.com/angular/in-memory-web-api/web-api.js'
},
// Test libraries
// Plunker recommends getting jasmine from cloudfare
// Don't upgrade to 2.5.x until following issue resolved
// https://github.com/jasmine/jasmine/issues/1231
{
pattern: 'script',
from: 'node_modules/jasmine-core/lib/jasmine-core/jasmine.js',
to: 'https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.4.1/jasmine.js'
},
{
pattern: 'script',
from: 'node_modules/jasmine-core/lib/jasmine-core/jasmine-html.js',
to: 'https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.4.1/jasmine-html.js'
},
{
pattern: 'script',
from: 'node_modules/jasmine-core/lib/jasmine-core/boot.js',
to: 'https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.4.1/boot.js'
},
{
pattern: 'link',
from: 'node_modules/jasmine-core/lib/jasmine-core/jasmine.css',
to: 'https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.4.1/jasmine.css'
},
{
pattern: 'angular_pkg',
},
{
pattern: 'zone_pkg',
},
// {
// pattern: 'system_strip_import_app',
// },
{
pattern: 'system_extra_main'
},
{
pattern: 'system_extra_defaultExtension'
}
];
module.exports = {
rules: rules,
rulesToApply: rulesToApply
};

View File

@ -0,0 +1,24 @@
var rules = {
system_extra_main: {
from: /main:\s*[\'|\"]index.js[\'|\"]/g,
to: 'main: "index.ts"'
},
system_extra_defaultExtension: {
from: /defaultExtension:\s*[\'|\"]js[\'|\"]/g,
to: 'defaultExtension: "ts"'
}
};
var rulesToApply = [
{
pattern: 'system_extra_main'
},
{
pattern: 'system_extra_defaultExtension'
}
];
module.exports = {
rules: rules,
rulesToApply: rulesToApply
};

View File

@ -170,6 +170,10 @@ JSONStream@^1.2.1:
jsonparse "^1.2.0"
through ">=2.2.7 <3"
abab@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.3.tgz#b81de5f7274ec4e756d797cd834f303642724e5d"
abbrev@1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f"
@ -187,6 +191,12 @@ acorn-dynamic-import@^2.0.0:
dependencies:
acorn "^4.0.3"
acorn-globals@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-3.1.0.tgz#fd8270f71fbb4996b004fa880ee5d46573a731bf"
dependencies:
acorn "^4.0.4"
acorn-jsx@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b"
@ -338,6 +348,10 @@ arr-flatten@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.1.tgz#e5ffe54d45e19f32f216e91eb99c8ce892bb604b"
array-equal@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
array-find-index@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
@ -1253,6 +1267,10 @@ content-disposition@0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4"
content-type-parser@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.1.tgz#c3e56988c53c65127fb46d4032a3a900246fdc94"
content-type@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed"
@ -1467,6 +1485,16 @@ csso@~2.3.1:
clap "^1.0.9"
source-map "^0.5.3"
cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0":
version "0.3.2"
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.2.tgz#b8036170c79f07a90ff2f16e22284027a243848b"
"cssstyle@>= 0.2.37 < 0.3.0":
version "0.2.37"
resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-0.2.37.tgz#541097234cb2513c83ceed3acddc27ff27987d54"
dependencies:
cssom "0.3.x"
csv-streamify@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/csv-streamify/-/csv-streamify-3.0.4.tgz#4cb614c57e3f299cca17b63fdcb4ad167777f47a"
@ -1954,6 +1982,17 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
escodegen@^1.6.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018"
dependencies:
esprima "^2.7.1"
estraverse "^1.9.1"
esutils "^2.0.2"
optionator "^0.8.1"
optionalDependencies:
source-map "~0.2.0"
escope@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3"
@ -2018,7 +2057,7 @@ espree@^3.4.0:
acorn "^5.0.1"
acorn-jsx "^3.0.0"
esprima@^2.6.0:
esprima@^2.6.0, esprima@^2.7.1:
version "2.7.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
@ -2035,6 +2074,10 @@ esrecurse@^4.1.0:
estraverse "~4.1.0"
object-assign "^4.0.1"
estraverse@^1.9.1:
version "1.9.3"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44"
estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
@ -2787,6 +2830,12 @@ html-comment-regex@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e"
html-encoding-sniffer@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.1.tgz#79bf7a785ea495fe66165e734153f363ff5437da"
dependencies:
whatwg-encoding "^1.0.1"
html-entities@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.0.tgz#41948caf85ce82fed36e4e6a0ed371a6664379e2"
@ -2889,6 +2938,10 @@ https-proxy-agent@^1.0.0:
debug "2"
extend "3"
iconv-lite@0.4.13:
version "0.4.13"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2"
iconv-lite@0.4.15:
version "0.4.15"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb"
@ -3361,6 +3414,30 @@ jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
jsdom@^9.12.0:
version "9.12.0"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-9.12.0.tgz#e8c546fffcb06c00d4833ca84410fed7f8a097d4"
dependencies:
abab "^1.0.3"
acorn "^4.0.4"
acorn-globals "^3.1.0"
array-equal "^1.0.0"
content-type-parser "^1.0.1"
cssom ">= 0.3.2 < 0.4.0"
cssstyle ">= 0.2.37 < 0.3.0"
escodegen "^1.6.1"
html-encoding-sniffer "^1.0.1"
nwmatcher ">= 1.3.9 < 2.0.0"
parse5 "^1.5.1"
request "^2.79.0"
sax "^1.2.1"
symbol-tree "^3.2.1"
tough-cookie "^2.3.2"
webidl-conversions "^4.0.0"
whatwg-encoding "^1.0.1"
whatwg-url "^4.3.0"
xml-name-validator "^2.0.1"
jsesc@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
@ -4181,6 +4258,10 @@ nunjucks@^2.4.2:
chokidar "^1.6.0"
yargs "^3.32.0"
"nwmatcher@>= 1.3.9 < 2.0.0":
version "1.3.9"
resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.3.9.tgz#8bab486ff7fa3dfd086656bbe8b17116d3692d2a"
oauth-sign@~0.8.1:
version "0.8.2"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
@ -4272,7 +4353,7 @@ optimist@~0.3, optimist@~0.3.5:
dependencies:
wordwrap "~0.0.2"
optionator@^0.8.2:
optionator@^0.8.1, optionator@^0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64"
dependencies:
@ -4378,6 +4459,10 @@ parse-json@^2.1.0, parse-json@^2.2.0:
dependencies:
error-ex "^1.2.0"
parse5@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-1.5.1.tgz#9b7f3b0de32be78dc2401b17573ccaf0f6f59d94"
parse5@^3.0.1:
version "3.0.2"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.2.tgz#05eff57f0ef4577fb144a79f8b9a967a6cc44510"
@ -5304,7 +5389,7 @@ sax@0.6.x:
version "0.6.1"
resolved "https://registry.yarnpkg.com/sax/-/sax-0.6.1.tgz#563b19c7c1de892e09bfc4f2fc30e3c27f0952b9"
sax@>=0.6.0, sax@~1.2.1:
sax@>=0.6.0, sax@^1.2.1, sax@~1.2.1:
version "1.2.2"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.2.tgz#fd8631a23bc7826bef5d871bdb87378c95647828"
@ -5569,6 +5654,12 @@ source-map@^0.4.4:
dependencies:
amdefine ">=0.0.4"
source-map@~0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d"
dependencies:
amdefine ">=0.0.4"
spdx-correct@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40"
@ -5824,6 +5915,10 @@ symbol-observable@^1.0.1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d"
symbol-tree@^3.2.1:
version "3.2.2"
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6"
table@^3.7.8:
version "3.8.3"
resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f"
@ -5966,7 +6061,7 @@ toposort@^1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.3.tgz#f02cd8a74bd8be2fc0e98611c3bacb95a171869c"
tough-cookie@~2.3.0:
tough-cookie@^2.3.2, tough-cookie@~2.3.0:
version "2.3.2"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a"
dependencies:
@ -5978,6 +6073,10 @@ toxic@^1.0.0:
dependencies:
lodash "^2.4.1"
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
trim-newlines@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
@ -6396,6 +6495,14 @@ webdriver-manager@^12.0.1:
semver "^5.3.0"
xml2js "^0.4.17"
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
webidl-conversions@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.1.tgz#8015a17ab83e7e1b311638486ace81da6ce206a0"
webpack-dev-middleware@^1.0.11, webpack-dev-middleware@^1.9.0:
version "1.10.1"
resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.10.1.tgz#c6b4cf428139cf1aefbe06a0c00fdb4f8da2f893"
@ -6475,6 +6582,19 @@ websocket-extensions@>=0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.1.tgz#76899499c184b6ef754377c2dbb0cd6cb55d29e7"
whatwg-encoding@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.1.tgz#3c6c451a198ee7aec55b1ec61d0920c67801a5f4"
dependencies:
iconv-lite "0.4.13"
whatwg-url@^4.3.0:
version "4.7.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-4.7.0.tgz#202035ac1955b087cdd20fa8b58ded3ab1cd2af5"
dependencies:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"
when@~3.6.x:
version "3.6.4"
resolved "https://registry.yarnpkg.com/when/-/when-3.6.4.tgz#473b517ec159e2b85005497a13983f095412e34e"
@ -6606,6 +6726,10 @@ xml-char-classes@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/xml-char-classes/-/xml-char-classes-1.0.0.tgz#64657848a20ffc5df583a42ad8a277b4512bbc4d"
xml-name-validator@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635"
xml2js@0.4.4:
version "0.4.4"
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.4.tgz#3111010003008ae19240eba17497b57c729c555d"