diff --git a/build-scripts/gulp/app.js b/build-scripts/gulp/app.js index 985067c71f..9fdf96cc04 100644 --- a/build-scripts/gulp/app.js +++ b/build-scripts/gulp/app.js @@ -24,7 +24,7 @@ gulp.task( gulp.parallel("gen-icons-app", "gen-icons-mdi"), "gen-pages-dev", "gen-index-app-dev", - gulp.series("create-test-translation", "build-translations") + "build-translations" ), "copy-static", "webpack-watch-app" diff --git a/build-scripts/gulp/gen-icons.js b/build-scripts/gulp/gen-icons.js index b116b8abea..8ce9f7612b 100644 --- a/build-scripts/gulp/gen-icons.js +++ b/build-scripts/gulp/gen-icons.js @@ -2,6 +2,7 @@ const gulp = require("gulp"); const path = require("path"); const fs = require("fs"); const paths = require("../paths"); +const { mapFiles } = require("../util"); const ICON_PACKAGE_PATH = path.resolve( __dirname, @@ -57,20 +58,6 @@ function generateIconset(iconsetName, iconNames) { return `${iconDefs}`; } -// Helper function to map recursively over files in a folder and it's subfolders -function mapFiles(startPath, filter, mapFunc) { - const files = fs.readdirSync(startPath); - for (let i = 0; i < files.length; i++) { - const filename = path.join(startPath, files[i]); - const stat = fs.lstatSync(filename); - if (stat.isDirectory()) { - mapFiles(filename, filter, mapFunc); - } else if (filename.indexOf(filter) >= 0) { - mapFunc(filename); - } - } -} - // Find all icons used by the project. function findIcons(searchPath, iconsetName) { const iconRegex = new RegExp(`${iconsetName}:[\\w-]+`, "g"); diff --git a/build-scripts/gulp/translations.js b/build-scripts/gulp/translations.js index a484a8f32b..80e2e72213 100755 --- a/build-scripts/gulp/translations.js +++ b/build-scripts/gulp/translations.js @@ -1,14 +1,17 @@ +const crypto = require("crypto"); const del = require("del"); const path = require("path"); +const source = require("vinyl-source-stream"); +const vinylBuffer = require("vinyl-buffer"); const gulp = require("gulp"); const fs = require("fs"); const foreach = require("gulp-foreach"); -const hash = require("gulp-hash"); -const hashFilename = require("gulp-hash-filename"); const merge = require("gulp-merge-json"); const minify = require("gulp-jsonminify"); const rename = require("gulp-rename"); const transform = require("gulp-json-transform"); +const { mapFiles } = require("../util"); +const env = require("../env"); const inDir = "translations"; const workDir = "build-translations"; @@ -39,8 +42,6 @@ const TRANSLATION_FRAGMENTS = [ "developer-tools", ]; -const tasks = []; - function recursiveFlatten(prefix, data) { let output = {}; Object.keys(data).forEach(function(key) { @@ -116,11 +117,9 @@ function lokaliseTransform(data, original, file) { return output; } -let taskName = "clean-translations"; -gulp.task(taskName, function() { - return del([`${outDir}/**/*.json`]); +gulp.task("clean-translations", function() { + return del([workDir]); }); -tasks.push(taskName); gulp.task("ensure-translations-build-dir", (done) => { if (!fs.existsSync(workDir)) { @@ -129,27 +128,21 @@ gulp.task("ensure-translations-build-dir", (done) => { done(); }); -taskName = "create-test-metadata"; -gulp.task( - taskName, - gulp.series("ensure-translations-build-dir", function writeTestMetaData(cb) { - fs.writeFile( - workDir + "/testMetadata.json", - JSON.stringify({ - test: { - nativeName: "Test", - }, - }), - cb - ); - }) -); -tasks.push(taskName); +gulp.task("create-test-metadata", function(cb) { + fs.writeFile( + workDir + "/testMetadata.json", + JSON.stringify({ + test: { + nativeName: "Test", + }, + }), + cb + ); +}); -taskName = "create-test-translation"; gulp.task( - taskName, - gulp.series("create-test-metadata", function() { + "create-test-translation", + gulp.series("create-test-metadata", function createTestTranslation() { return gulp .src("src/translations/en.json") .pipe( @@ -161,7 +154,6 @@ gulp.task( .pipe(gulp.dest(workDir)); }) ); -tasks.push(taskName); /** * This task will build a master translation file, to be used as the base for @@ -172,235 +164,215 @@ tasks.push(taskName); * project is buildable immediately after merging new translation keys, since * the Lokalise update to translations/en.json will not happen immediately. */ -taskName = "build-master-translation"; -gulp.task( - taskName, - gulp.series("clean-translations", function() { - return gulp - .src("src/translations/en.json") - .pipe( - transform(function(data, file) { - return lokaliseTransform(data, data, file); - }) - ) - .pipe(rename("translationMaster.json")) - .pipe(gulp.dest(workDir)); - }) -); -tasks.push(taskName); +gulp.task("build-master-translation", function() { + return gulp + .src("src/translations/en.json") + .pipe( + transform(function(data, file) { + return lokaliseTransform(data, data, file); + }) + ) + .pipe(rename("translationMaster.json")) + .pipe(gulp.dest(workDir)); +}); -taskName = "build-merged-translations"; -gulp.task( - taskName, - gulp.series("build-master-translation", function() { - return gulp - .src([inDir + "/*.json", workDir + "/test.json"], { allowEmpty: true }) - .pipe( - transform(function(data, file) { - return lokaliseTransform(data, data, file); - }) - ) - .pipe( - foreach(function(stream, file) { - // For each language generate a merged json file. It begins with the master - // translation as a failsafe for untranslated strings, and merges all parent - // tags into one file for each specific subtag - // - // TODO: This is a naive interpretation of BCP47 that should be improved. - // Will be OK for now as long as we don't have anything more complicated - // than a base translation + region. - const tr = path.basename(file.history[0], ".json"); - const subtags = tr.split("-"); - const src = [workDir + "/translationMaster.json"]; - for (let i = 1; i <= subtags.length; i++) { - const lang = subtags.slice(0, i).join("-"); - if (lang === "test") { - src.push(workDir + "/test.json"); - } else if (lang !== "en") { - src.push(inDir + "/" + lang + ".json"); - } +gulp.task("build-merged-translations", function() { + return gulp + .src([inDir + "/*.json", workDir + "/test.json"], { allowEmpty: true }) + .pipe( + transform(function(data, file) { + return lokaliseTransform(data, data, file); + }) + ) + .pipe( + foreach(function(stream, file) { + // For each language generate a merged json file. It begins with the master + // translation as a failsafe for untranslated strings, and merges all parent + // tags into one file for each specific subtag + // + // TODO: This is a naive interpretation of BCP47 that should be improved. + // Will be OK for now as long as we don't have anything more complicated + // than a base translation + region. + const tr = path.basename(file.history[0], ".json"); + const subtags = tr.split("-"); + const src = [workDir + "/translationMaster.json"]; + for (let i = 1; i <= subtags.length; i++) { + const lang = subtags.slice(0, i).join("-"); + if (lang === "test") { + src.push(workDir + "/test.json"); + } else if (lang !== "en") { + src.push(inDir + "/" + lang + ".json"); } - return gulp - .src(src, { allowEmpty: true }) - .pipe(transform((data) => emptyFilter(data))) - .pipe( - merge({ - fileName: tr + ".json", - }) - ) - .pipe(gulp.dest(fullDir)); - }) - ); - }) -); -tasks.push(taskName); + } + return gulp + .src(src, { allowEmpty: true }) + .pipe(transform((data) => emptyFilter(data))) + .pipe( + merge({ + fileName: tr + ".json", + }) + ) + .pipe(gulp.dest(fullDir)); + }) + ); +}); + +var taskName; const splitTasks = []; TRANSLATION_FRAGMENTS.forEach((fragment) => { taskName = "build-translation-fragment-" + fragment; - gulp.task( - taskName, - gulp.series("build-merged-translations", function() { - // Return only the translations for this fragment. - return gulp - .src(fullDir + "/*.json") - .pipe( - transform((data) => ({ - ui: { - panel: { - [fragment]: data.ui.panel[fragment], - }, + gulp.task(taskName, function() { + // Return only the translations for this fragment. + return gulp + .src(fullDir + "/*.json") + .pipe( + transform((data) => ({ + ui: { + panel: { + [fragment]: data.ui.panel[fragment], }, - })) - ) - .pipe(gulp.dest(workDir + "/" + fragment)); - }) - ); - tasks.push(taskName); + }, + })) + ) + .pipe(gulp.dest(workDir + "/" + fragment)); + }); splitTasks.push(taskName); }); taskName = "build-translation-core"; -gulp.task( - taskName, - gulp.series("build-merged-translations", function() { - // Remove the fragment translations from the core translation. - return gulp - .src(fullDir + "/*.json") - .pipe( - transform((data) => { - TRANSLATION_FRAGMENTS.forEach((fragment) => { - delete data.ui.panel[fragment]; - }); - return data; - }) - ) - .pipe(gulp.dest(coreDir)); - }) -); -tasks.push(taskName); +gulp.task(taskName, function() { + // Remove the fragment translations from the core translation. + return gulp + .src(fullDir + "/*.json") + .pipe( + transform((data) => { + TRANSLATION_FRAGMENTS.forEach((fragment) => { + delete data.ui.panel[fragment]; + }); + return data; + }) + ) + .pipe(gulp.dest(coreDir)); +}); + splitTasks.push(taskName); -taskName = "build-flattened-translations"; -gulp.task( - taskName, - gulp.series(...splitTasks, function() { - // Flatten the split versions of our translations, and move them into outDir - return gulp - .src( - TRANSLATION_FRAGMENTS.map( - (fragment) => workDir + "/" + fragment + "/*.json" - ).concat(coreDir + "/*.json"), - { base: workDir } - ) - .pipe( - transform(function(data) { - // Polymer.AppLocalizeBehavior requires flattened json - return flatten(data); - }) - ) - .pipe(minify()) - .pipe(hashFilename()) - .pipe( - rename((filePath) => { - if (filePath.dirname === "core") { - filePath.dirname = ""; - } - }) - ) - .pipe(gulp.dest(outDir)); - }) -); -tasks.push(taskName); +gulp.task("build-flattened-translations", function() { + // Flatten the split versions of our translations, and move them into outDir + return gulp + .src( + TRANSLATION_FRAGMENTS.map( + (fragment) => workDir + "/" + fragment + "/*.json" + ).concat(coreDir + "/*.json"), + { base: workDir } + ) + .pipe( + transform(function(data) { + // Polymer.AppLocalizeBehavior requires flattened json + return flatten(data); + }) + ) + .pipe(minify()) + .pipe( + rename((filePath) => { + if (filePath.dirname === "core") { + filePath.dirname = ""; + } + }) + ) + .pipe(gulp.dest(outDir)); +}); -taskName = "build-translation-fingerprints"; -gulp.task( - taskName, - gulp.series("build-flattened-translations", function() { - return gulp - .src(outDir + "/**/*.json") - .pipe( - rename({ - extname: "", - }) - ) - .pipe( - hash({ - algorithm: "md5", - hashLength: 32, - template: "<%= name %>.json", - }) - ) - .pipe(hash.manifest("translationFingerprints.json")) - .pipe( - transform(function(data) { - // After generating fingerprints of our translation files, consolidate - // all translation fragment fingerprints under the translation name key - const newData = {}; - Object.entries(data).forEach(([key, value]) => { - const [path, _md5] = key.rsplit("-", 1); - // let translation = key; - let translation = path; - const parts = translation.split("/"); - if (parts.length === 2) { - translation = parts[1]; - } - if (!(translation in newData)) { - newData[translation] = { - fingerprints: {}, - }; - } - newData[translation].fingerprints[path] = value; - }); - return newData; - }) - ) - .pipe(gulp.dest(workDir)); - }) -); -tasks.push(taskName); +const fingerprints = {}; -taskName = "build-translations"; gulp.task( - taskName, - gulp.series("build-translation-fingerprints", function() { - return gulp - .src( - [ - "src/translations/translationMetadata.json", - workDir + "/testMetadata.json", - workDir + "/translationFingerprints.json", - ], - { allowEmpty: true } - ) - .pipe(merge({})) - .pipe( - transform(function(data) { - const newData = {}; - Object.entries(data).forEach(([key, value]) => { - // Filter out translations without native name. - if (data[key].nativeName) { - newData[key] = data[key]; - } else { - console.warn( - `Skipping language ${key}. Native name was not translated.` - ); - } - if (data[key]) newData[key] = value; - }); - return newData; - }) - ) - .pipe( - transform((data) => ({ - fragments: TRANSLATION_FRAGMENTS, - translations: data, - })) - ) - .pipe(rename("translationMetadata.json")) - .pipe(gulp.dest(workDir)); - }) -); -tasks.push(taskName); + "build-translation-fingerprints", + function fingerprintTranslationFiles() { + // Fingerprint full file of each language + const files = fs.readdirSync(fullDir); + for (let i = 0; i < files.length; i++) { + fingerprints[files[i].split(".")[0]] = { + // In dev we create fake hashes + hash: env.isProdBuild + ? crypto + .createHash("md5") + .update(fs.readFileSync(path.join(fullDir, files[i]), "utf-8")) + .digest("hex") + : "dev", + }; + } -module.exports = tasks; + mapFiles(outDir, ".json", (filename) => { + const parsed = path.parse(filename); + + // nl.json -> nl-.json + if (!(parsed.name in fingerprints)) { + throw new Error(`Unable to find hash for ${filename}`); + } + + fs.renameSync( + filename, + `${parsed.dir}/${parsed.name}-${fingerprints[parsed.name].hash}${ + parsed.ext + }` + ); + }); + + const stream = source("translationFingerprints.json"); + stream.write(JSON.stringify(fingerprints)); + process.nextTick(() => stream.end()); + return stream.pipe(vinylBuffer()).pipe(gulp.dest(workDir)); + } +); + +gulp.task( + "build-translations", + gulp.series( + "clean-translations", + "ensure-translations-build-dir", + env.isProdBuild ? (done) => done() : "create-test-translation", + "build-master-translation", + "build-merged-translations", + gulp.parallel(...splitTasks), + "build-flattened-translations", + "build-translation-fingerprints", + function writeMetadata() { + return gulp + .src( + [ + "src/translations/translationMetadata.json", + workDir + "/testMetadata.json", + workDir + "/translationFingerprints.json", + ], + { allowEmpty: true } + ) + .pipe(merge({})) + .pipe( + transform(function(data) { + const newData = {}; + Object.entries(data).forEach(([key, value]) => { + // Filter out translations without native name. + if (data[key].nativeName) { + newData[key] = data[key]; + } else { + console.warn( + `Skipping language ${key}. Native name was not translated.` + ); + } + if (data[key]) newData[key] = value; + }); + return newData; + }) + ) + .pipe( + transform((data) => ({ + fragments: TRANSLATION_FRAGMENTS, + translations: data, + })) + ) + .pipe(rename("translationMetadata.json")) + .pipe(gulp.dest(workDir)); + } + ) +); diff --git a/build-scripts/util.js b/build-scripts/util.js new file mode 100644 index 0000000000..23efdfb229 --- /dev/null +++ b/build-scripts/util.js @@ -0,0 +1,16 @@ +const path = require("path"); +const fs = require("fs"); + +// Helper function to map recursively over files in a folder and it's subfolders +module.exports.mapFiles = function mapFiles(startPath, filter, mapFunc) { + const files = fs.readdirSync(startPath); + for (let i = 0; i < files.length; i++) { + const filename = path.join(startPath, files[i]); + const stat = fs.lstatSync(filename); + if (stat.isDirectory()) { + mapFiles(filename, filter, mapFunc); + } else if (filename.indexOf(filter) >= 0) { + mapFunc(filename); + } + } +}; diff --git a/build-scripts/webpack.js b/build-scripts/webpack.js index 042d978461..88ad6ac1dc 100644 --- a/build-scripts/webpack.js +++ b/build-scripts/webpack.js @@ -148,11 +148,17 @@ const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => { // Create an object mapping browser urls to their paths during build const translationMetadata = require("../build-translations/translationMetadata.json"); const workBoxTranslationsTemplatedURLs = {}; - const englishFP = translationMetadata.translations.en.fingerprints; - Object.keys(englishFP).forEach((key) => { + const englishFilename = `en-${translationMetadata.translations.en.hash}.json`; + + // core + workBoxTranslationsTemplatedURLs[ + `/static/translations/${englishFilename}` + ] = `build-translations/output/${englishFilename}`; + + Object.keys(translationMetadata.fragments).forEach((fragment) => { workBoxTranslationsTemplatedURLs[ - `/static/translations/${englishFP[key]}` - ] = `build-translations/output/${key}.json`; + `/static/translations/${fragment}/${englishFilename}` + ] = `build-translations/output/${fragment}/${englishFilename}`; }); config.plugins.push( diff --git a/package.json b/package.json index 09facfc322..9c1c133861 100644 --- a/package.json +++ b/package.json @@ -145,13 +145,11 @@ "fs-extra": "^7.0.1", "gulp": "^4.0.0", "gulp-foreach": "^0.1.0", - "gulp-hash": "^4.2.2", - "gulp-hash-filename": "^2.0.1", "gulp-insert": "^0.5.0", "gulp-json-transform": "^0.4.6", "gulp-jsonminify": "^1.1.0", "gulp-merge-json": "^1.3.1", - "gulp-rename": "^1.4.0", + "gulp-rename": "^2.0.0", "gulp-zopfli-green": "^3.0.1", "html-loader": "^0.5.5", "html-webpack-plugin": "^3.2.0", @@ -174,6 +172,8 @@ "tslint-eslint-rules": "^5.4.0", "tslint-plugin-prettier": "^2.0.1", "typescript": "^3.7.2", + "vinyl-buffer": "^1.0.1", + "vinyl-source-stream": "^2.0.0", "web-component-tester": "^6.9.2", "webpack": "^4.40.2", "webpack-cli": "^3.3.9", diff --git a/src/translations/translationMetadata.json b/src/translations/translationMetadata.json index 83f61b012d..908a8fb577 100644 --- a/src/translations/translationMetadata.json +++ b/src/translations/translationMetadata.json @@ -34,7 +34,7 @@ "nativeName": "English" }, "eo": { - "navtiveName": "Esperanto" + "nativeName": "Esperanto" }, "es": { "nativeName": "EspaƱol" diff --git a/src/types.ts b/src/types.ts index 13e7a358fd..90ba298389 100644 --- a/src/types.ts +++ b/src/types.ts @@ -95,7 +95,7 @@ export interface Panels { export interface Translation { nativeName: string; isRTL: boolean; - fingerprints: { [fragment: string]: string }; + hash: string; } export interface TranslationMetadata { diff --git a/src/util/hass-translation.ts b/src/util/hass-translation.ts index a792de7362..5378428402 100644 --- a/src/util/hass-translation.ts +++ b/src/util/hass-translation.ts @@ -122,8 +122,11 @@ export async function getTranslation( } throw new Error("Language en is not found in metadata"); } - const fingerprint = - metadata.fingerprints[fragment ? `${fragment}/${language}` : language]; + + // nl-abcd.jon or logbook/nl-abcd.json + const fingerprint = `${fragment ? fragment + "/" : ""}${language}-${ + metadata.hash + }.json`; // Fetch translation from the server if (!translations[fingerprint]) { diff --git a/yarn.lock b/yarn.lock index 4ca792c516..d2d859218e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3968,7 +3968,7 @@ binary-extensions@^1.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== -bl@^1.0.0: +bl@^1.0.0, bl@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c" integrity sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA== @@ -5951,11 +5951,6 @@ es6-object-assign@^1.1.0: resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw= -es6-promise@^2.1.1: - version "2.3.0" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-2.3.0.tgz#96edb9f2fdb01995822b263dd8aadab6748181bc" - integrity sha1-lu258v2wGZWCKyY92KratnSBgbw= - es6-promise@^4.0.3, es6-promise@^4.0.5: version "4.2.6" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.6.tgz#b685edd8258886365ea62b57d30de28fadcd974f" @@ -7261,22 +7256,6 @@ gulp-foreach@^0.1.0: gulp-util "~2.2.14" through2 "~0.6.3" -gulp-hash-filename@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/gulp-hash-filename/-/gulp-hash-filename-2.0.1.tgz#c30656261a9b622d636766e48b8297125b4ddde8" - integrity sha512-pMg5owb8Dt0wqjgPx/TFbU3c5ckD16rrgo0BTm9PQ3pVC1Zsgw7AYx1+DP2t31JoUTeN1/dPuXNWnCNvN/wj7A== - -gulp-hash@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/gulp-hash/-/gulp-hash-4.2.2.tgz#2cf4ad081ef7a65393a51e3df58f514f388f4523" - integrity sha512-uWCjiy7ZXCPu4aaTM454+ImCnrR+07MKdwpunDZU0I7oUd5+SfyDxxhYzgpzSRVSlCFWfBBqNY9vAL3m3F+3uw== - dependencies: - es6-promise "^2.1.1" - lodash.assign "^2.4.1" - lodash.template "^2.4.1" - through2 "^2.0.0" - vinyl "^2.1.0" - gulp-if@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/gulp-if/-/gulp-if-2.0.2.tgz#a497b7e7573005041caa2bc8b7dda3c80444d629" @@ -7334,10 +7313,10 @@ gulp-merge-json@^1.3.1: through "^2.3.8" vinyl "^2.1.0" -gulp-rename@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/gulp-rename/-/gulp-rename-1.4.0.tgz#de1c718e7c4095ae861f7296ef4f3248648240bd" - integrity sha512-swzbIGb/arEoFK89tPY58vg3Ok1bw+d35PfUNwWqdo7KM4jkmuGA78JiDNqR+JeZFaeeHnRg9N7aihX3YPmsyg== +gulp-rename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/gulp-rename/-/gulp-rename-2.0.0.tgz#9bbc3962b0c0f52fc67cd5eaff6c223ec5b9cf6c" + integrity sha512-97Vba4KBzbYmR5VBs9mWmK+HwIf5mj+/zioxfZhOKeXtx5ZjBk57KFlePf5nxq9QsTtFl0ejnHE3zTC9MHXqyQ== gulp-sourcemaps@1.6.0: version "1.6.0" @@ -8844,55 +8823,6 @@ locate-path@^3.0.0: p-locate "^3.0.0" path-exists "^3.0.0" -lodash._basebind@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._basebind/-/lodash._basebind-2.4.1.tgz#e940b9ebdd27c327e0a8dab1b55916c5341e9575" - integrity sha1-6UC5690nwyfgqNqxtVkWxTQelXU= - dependencies: - lodash._basecreate "~2.4.1" - lodash._setbinddata "~2.4.1" - lodash._slice "~2.4.1" - lodash.isobject "~2.4.1" - -lodash._basecreate@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-2.4.1.tgz#f8e6f5b578a9e34e541179b56b8eeebf4a287e08" - integrity sha1-+Ob1tXip405UEXm1a47uv0oofgg= - dependencies: - lodash._isnative "~2.4.1" - lodash.isobject "~2.4.1" - lodash.noop "~2.4.1" - -lodash._basecreatecallback@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._basecreatecallback/-/lodash._basecreatecallback-2.4.1.tgz#7d0b267649cb29e7a139d0103b7c11fae84e4851" - integrity sha1-fQsmdknLKeehOdAQO3wR+uhOSFE= - dependencies: - lodash._setbinddata "~2.4.1" - lodash.bind "~2.4.1" - lodash.identity "~2.4.1" - lodash.support "~2.4.1" - -lodash._basecreatewrapper@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._basecreatewrapper/-/lodash._basecreatewrapper-2.4.1.tgz#4d31f2e7de7e134fbf2803762b8150b32519666f" - integrity sha1-TTHy595+E0+/KAN2K4FQsyUZZm8= - dependencies: - lodash._basecreate "~2.4.1" - lodash._setbinddata "~2.4.1" - lodash._slice "~2.4.1" - lodash.isobject "~2.4.1" - -lodash._createwrapper@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._createwrapper/-/lodash._createwrapper-2.4.1.tgz#51d6957973da4ed556e37290d8c1a18c53de1607" - integrity sha1-UdaVeXPaTtVW43KQ2MGhjFPeFgc= - dependencies: - lodash._basebind "~2.4.1" - lodash._basecreatewrapper "~2.4.1" - lodash._slice "~2.4.1" - lodash.isfunction "~2.4.1" - lodash._escapehtmlchar@~2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/lodash._escapehtmlchar/-/lodash._escapehtmlchar-2.4.1.tgz#df67c3bb6b7e8e1e831ab48bfa0795b92afe899d" @@ -8938,14 +8868,6 @@ lodash._reunescapedhtml@~2.4.1: lodash._htmlescapes "~2.4.1" lodash.keys "~2.4.1" -lodash._setbinddata@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._setbinddata/-/lodash._setbinddata-2.4.1.tgz#f7c200cd1b92ef236b399eecf73c648d17aa94d2" - integrity sha1-98IAzRuS7yNrOZ7s9zxkjReqlNI= - dependencies: - lodash._isnative "~2.4.1" - lodash.noop "~2.4.1" - lodash._shimkeys@~2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz#6e9cc9666ff081f0b5a6c978b83e242e6949d203" @@ -8953,28 +8875,6 @@ lodash._shimkeys@~2.4.1: dependencies: lodash._objecttypes "~2.4.1" -lodash._slice@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._slice/-/lodash._slice-2.4.1.tgz#745cf41a53597b18f688898544405efa2b06d90f" - integrity sha1-dFz0GlNZexj2iImFREBe+isG2Q8= - -lodash.assign@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-2.4.1.tgz#84c39596dd71181a97b0652913a7c9675e49b1aa" - integrity sha1-hMOVlt1xGBqXsGUpE6fJZ15Jsao= - dependencies: - lodash._basecreatecallback "~2.4.1" - lodash._objecttypes "~2.4.1" - lodash.keys "~2.4.1" - -lodash.bind@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-2.4.1.tgz#5d19fa005c8c4d236faf4742c7b7a1fcabe29267" - integrity sha1-XRn6AFyMTSNvr0dCx7eh/Kvikmc= - dependencies: - lodash._createwrapper "~2.4.1" - lodash._slice "~2.4.1" - lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" @@ -9002,21 +8902,11 @@ lodash.escape@~2.4.1: lodash._reunescapedhtml "~2.4.1" lodash.keys "~2.4.1" -lodash.identity@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.identity/-/lodash.identity-2.4.1.tgz#6694cffa65fef931f7c31ce86c74597cf560f4f1" - integrity sha1-ZpTP+mX++TH3wxzobHRZfPVg9PE= - lodash.isequal@^4.0.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= -lodash.isfunction@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-2.4.1.tgz#2cfd575c73e498ab57e319b77fa02adef13a94d1" - integrity sha1-LP1XXHPkmKtX4xm3f6Aq3vE6lNE= - lodash.isobject@~2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-2.4.1.tgz#5a2e47fe69953f1ee631a7eba1fe64d2d06558f5" @@ -9043,11 +8933,6 @@ lodash.mergewith@^4.6.1: resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== -lodash.noop@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.noop/-/lodash.noop-2.4.1.tgz#4fb54f816652e5ae10e8f72f717a388c7326538a" - integrity sha1-T7VPgWZS5a4Q6PcvcXo4jHMmU4o= - lodash.padend@^4.6.1: version "4.6.1" resolved "https://registry.yarnpkg.com/lodash.padend/-/lodash.padend-4.6.1.tgz#53ccba047d06e158d311f45da625f4e49e6f166e" @@ -9063,13 +8948,6 @@ lodash.sortby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= -lodash.support@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.support/-/lodash.support-2.4.1.tgz#320e0b67031673c28d7a2bb5d9e0331a45240515" - integrity sha1-Mg4LZwMWc8KNeiu12eAzGkUkBRU= - dependencies: - lodash._isnative "~2.4.1" - lodash.template@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-2.4.1.tgz#9e611007edf629129a974ab3c48b817b3e1cf20d" @@ -13613,6 +13491,14 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +vinyl-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/vinyl-buffer/-/vinyl-buffer-1.0.1.tgz#96c1a3479b8c5392542c612029013b5b27f88bbf" + integrity sha1-lsGjR5uMU5JULGEgKQE7Wyf4i78= + dependencies: + bl "^1.2.1" + through2 "^2.0.3" + vinyl-fs@^2.4.4: version "2.4.4" resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-2.4.4.tgz#be6ff3270cb55dfd7d3063640de81f25d7532239" @@ -13659,6 +13545,14 @@ vinyl-fs@^3.0.0: vinyl "^2.0.0" vinyl-sourcemap "^1.1.0" +vinyl-source-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/vinyl-source-stream/-/vinyl-source-stream-2.0.0.tgz#f38a5afb9dd1e93b65d550469ac6182ac4f54b8e" + integrity sha1-84pa+53R6Ttl1VBGmsYYKsT1S44= + dependencies: + through2 "^2.0.3" + vinyl "^2.1.0" + vinyl-sourcemap@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz#92a800593a38703a8cdb11d8b300ad4be63b3e16"