From b11de2734e86b708e9d1bd934c176b2080712698 Mon Sep 17 00:00:00 2001 From: Chris Breiding Date: Wed, 20 May 2020 10:59:36 -0400 Subject: [PATCH 1/8] remove vscode setting editor.formatOnSave MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit causes double formatting and certain rules to be applied incorrectly if the user’s ‘global’ vscode settings also have `formatOnSave` turned on --- .vscode/settings.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 2730904..04bc424 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ { "eslint.enable": true, - "editor.formatOnSave": true, // enable for eslint-plugin json-format - "eslint.validate": ["json"], + "eslint.validate": [ + "json" + ], } From 59ab7d9f899342a59feee3e47af2b67b005f87cd Mon Sep 17 00:00:00 2001 From: Chris Breiding Date: Wed, 20 May 2020 11:13:03 -0400 Subject: [PATCH 2/8] eslint auto-formatting --- test/unit/index_spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/unit/index_spec.js b/test/unit/index_spec.js index 2ae712c..e599b73 100644 --- a/test/unit/index_spec.js +++ b/test/unit/index_spec.js @@ -204,7 +204,7 @@ describe('browserify preprocessor', function () { }) it('uses transforms if provided', function () { - const transform = [() => {}, {}] + const transform = [() => { }, {}] this.options.browserifyOptions = { transform } @@ -334,7 +334,7 @@ describe('browserify preprocessor', function () { } this.run().then(() => { - streamApi.on.withArgs('error').yieldsAsync(new Error('bundle error')).returns({ pipe () {} }) + streamApi.on.withArgs('error').yieldsAsync(new Error('bundle error')).returns({ pipe () { } }) this.bundlerApi.on.withArgs('update').yield() }) }) @@ -345,7 +345,7 @@ describe('browserify preprocessor', function () { return run(this.file) .then(() => { - streamApi.on.withArgs('error').yieldsAsync(new Error('bundle error')).returns({ pipe () {} }) + streamApi.on.withArgs('error').yieldsAsync(new Error('bundle error')).returns({ pipe () { } }) this.bundlerApi.on.withArgs('update').yield() return run(this.file) From 006355a68f40e689bf32c26ceb1fba67a492e772 Mon Sep 17 00:00:00 2001 From: Chris Breiding Date: Wed, 20 May 2020 11:14:02 -0400 Subject: [PATCH 3/8] upgrade to fs-extra 9.0.0 and remove promisifying --- index.js | 4 ++-- lib/fs.js | 3 --- package.json | 2 +- test/e2e/e2e_spec.js | 4 ++-- test/unit/index_spec.js | 6 +++--- 5 files changed, 8 insertions(+), 11 deletions(-) delete mode 100644 lib/fs.js diff --git a/index.js b/index.js index 757cfd5..f751cef 100644 --- a/index.js +++ b/index.js @@ -2,7 +2,7 @@ const path = require('path') const Promise = require('bluebird') -const fs = require('./lib/fs') +const fs = require('fs-extra') const cloneDeep = require('lodash.clonedeep') const browserify = require('browserify') @@ -227,7 +227,7 @@ const preprocessor = (options = {}) => { }) const bundlePromise = fs - .ensureDirAsync(path.dirname(outputPath)) + .ensureDir(path.dirname(outputPath)) .then(bundle) // cache the bundle promise, so it can be returned if this function diff --git a/lib/fs.js b/lib/fs.js deleted file mode 100644 index 693cfb3..0000000 --- a/lib/fs.js +++ /dev/null @@ -1,3 +0,0 @@ -const Promise = require('bluebird') - -module.exports = Promise.promisifyAll(require('fs-extra')) diff --git a/package.json b/package.json index b66570c..e043fae 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "coffeeify": "3.0.1", "coffeescript": "1.12.7", "debug": "4.1.1", - "fs-extra": "7.0.1", + "fs-extra": "9.0.0", "lodash.clonedeep": "4.5.0", "through2": "^2.0.0", "watchify": "3.11.1" diff --git a/test/e2e/e2e_spec.js b/test/e2e/e2e_spec.js index 685a673..90d3891 100644 --- a/test/e2e/e2e_spec.js +++ b/test/e2e/e2e_spec.js @@ -1,12 +1,12 @@ const _ = require('lodash') const chai = require('chai') +const fs = require('fs-extra') const path = require('path') const snapshot = require('snap-shot-it') const Bluebird = require('bluebird') process.env.__TESTING__ = true -const fs = require('../../lib/fs') const preprocessor = require('../../index') /* eslint-disable-next-line no-unused-vars */ @@ -49,7 +49,7 @@ const verifySourceContents = ({ sources, sourcesContent }) => { const zippedArrays = _.zip(sources, sourcesContent) return Bluebird.map(zippedArrays, ([sourcePath, sourceContent]) => { - return fs.readFileAsync(sourcePath, 'utf8') + return fs.readFile(sourcePath, 'utf8') .then((str) => { expect(str).to.eq(sourceContent) }) diff --git a/test/unit/index_spec.js b/test/unit/index_spec.js index e599b73..be99068 100644 --- a/test/unit/index_spec.js +++ b/test/unit/index_spec.js @@ -1,6 +1,7 @@ 'use strict' const chai = require('chai') +const fs = require('fs-extra') const mockery = require('mockery') const sinon = require('sinon') const watchify = require('watchify') @@ -28,7 +29,6 @@ streamApi.on = sandbox.stub().returns(streamApi) process.env.__TESTING__ = true -const fs = require('../../lib/fs') const preprocessor = require('../../index') describe('browserify preprocessor', function () { @@ -54,7 +54,7 @@ describe('browserify preprocessor', function () { } sandbox.stub(fs, 'createWriteStream').returns(this.createWriteStreamApi) - sandbox.stub(fs, 'ensureDirAsync').resolves() + sandbox.stub(fs, 'ensureDir').resolves() this.options = {} this.file = { @@ -215,7 +215,7 @@ describe('browserify preprocessor', function () { it('ensures directory for output is created', function () { return this.run().then(() => { - expect(fs.ensureDirAsync).to.be.calledWith('output') + expect(fs.ensureDir).to.be.calledWith('output') }) }) From ad88970447f2cd07a8c3afe77494744a6dfa3663 Mon Sep 17 00:00:00 2001 From: Chris Breiding Date: Wed, 20 May 2020 11:18:46 -0400 Subject: [PATCH 4/8] major: Require Node.js 8 or greater MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit going to start using async/await, which requires Node.js 7.6+, so we’ll require the next stable version (8) or greater --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e043fae..53aa4d4 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "lib/*.js" ], "engines": { - "node": ">=6.5" + "node": ">=8" }, "license": "MIT", "repository": { From 3fb7b2cc3d57a7331cedb519d9f3b7188d11aa53 Mon Sep 17 00:00:00 2001 From: Chris Breiding Date: Wed, 20 May 2020 11:20:05 -0400 Subject: [PATCH 5/8] fix: validate type of typescript option and its existence as a path --- index.js | 16 +++++++++++++--- test/unit/index_spec.js | 42 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index f751cef..880f1f0 100644 --- a/index.js +++ b/index.js @@ -60,7 +60,7 @@ const defaultOptions = { }, } -const getBrowserifyOptions = (entry, userBrowserifyOptions = {}, typescriptPath = null) => { +const getBrowserifyOptions = async (entry, userBrowserifyOptions = {}, typescriptPath = null) => { let browserifyOptions = cloneDeep(defaultOptions.browserifyOptions) // allow user to override default options @@ -82,6 +82,16 @@ const getBrowserifyOptions = (entry, userBrowserifyOptions = {}, typescriptPath }) if (typescriptPath) { + if (typeof typescriptPath !== 'string') { + throw new Error(`The 'typescript' option must be a string. You passed: ${typescriptPath}`) + } + + const pathExists = await fs.pathExists(typescriptPath) + + if (!pathExists) { + throw new Error(`The 'typescript' option must be a valid path to your TypeScript installation. We could not find anything at the following path: ${typescriptPath}`) + } + const transform = browserifyOptions.transform const hasTsifyTransform = transform.some(([name]) => name.includes('tsify')) const hastsifyPlugin = browserifyOptions.plugin.includes('tsify') @@ -135,7 +145,7 @@ const preprocessor = (options = {}) => { // when running in the GUI, it will likely get called multiple times // with the same filePath, as the user could re-run the tests, causing // the supported file and spec file to be requested again - return (file) => { + return async (file) => { const filePath = file.filePath debug('get:', filePath) @@ -157,7 +167,7 @@ const preprocessor = (options = {}) => { debug('input:', filePath) debug('output:', outputPath) - const browserifyOptions = getBrowserifyOptions(filePath, options.browserifyOptions, options.typescript) + const browserifyOptions = await getBrowserifyOptions(filePath, options.browserifyOptions, options.typescript) const watchifyOptions = Object.assign({}, defaultOptions.watchifyOptions, options.watchifyOptions) const bundler = browserify(browserifyOptions) diff --git a/test/unit/index_spec.js b/test/unit/index_spec.js index be99068..5b729f5 100644 --- a/test/unit/index_spec.js +++ b/test/unit/index_spec.js @@ -424,23 +424,55 @@ describe('browserify preprocessor', function () { }) }) - describe('when typescript path and tsify are given together', function () { - it('throws error when it is a plugin', function () { + describe('validation', function () { + const shouldntResolve = () => { + throw new Error('Should error, should not resolve') + } + + it('throws error when typescript path is not a string', function () { + this.options.typescript = true + + return this.run() + .then(shouldntResolve) + .catch((err) => { + expect(err.message).to.equal(`The 'typescript' option must be a string. You passed: true`) + }) + }) + + it('throws error when nothing exists at typescript path', function () { + this.options.typescript = '/nothing/here' + + return this.run() + .then(shouldntResolve) + .catch((err) => { + expect(err.message).to.equal(`The 'typescript' option must be a valid path to your TypeScript installation. We could not find anything at the following path: /nothing/here`) + }) + }) + + it('throws error when typescript path and tsify plugin are specified', function () { this.options.browserifyOptions = { plugin: ['tsify'], } - expect(this.run).to.throw('This may cause conflicts') + return this.run() + .then(shouldntResolve) + .catch((err) => { + expect(err.message).to.include('This may cause conflicts') + }) }) - it('throws error when it is a transform', function () { + it('throws error when typescript path and tsify transform are specified', function () { this.options.browserifyOptions = { transform: [ ['path/to/tsify', {}], ], } - expect(this.run).to.throw('This may cause conflicts') + return this.run() + .then(shouldntResolve) + .catch((err) => { + expect(err.message).to.include('This may cause conflicts') + }) }) }) }) From 772548d1ea81b0682a51a76a70ed09b01bc72a5c Mon Sep 17 00:00:00 2001 From: Chris Breiding Date: Thu, 21 May 2020 09:37:09 -0400 Subject: [PATCH 6/8] standardize errros and include type --- index.js | 39 ++++++++++++++++++++++++++++++++------- test/unit/index_spec.js | 22 ++++++++++++++++++---- 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/index.js b/index.js index 880f1f0..2464300 100644 --- a/index.js +++ b/index.js @@ -10,6 +10,13 @@ const watchify = require('watchify') const debug = require('debug')('cypress:browserify') +const typescriptExtensionRegex = /\.tsx?$/ +const errorTypes = { + TYPESCRIPT_AND_TSIFY: 'TYPESCRIPT_AND_TSIFY', + TYPESCRIPT_NONEXISTENT: 'TYPESCRIPT_NONEXISTENT', + TYPESCRIPT_NOT_STRING: 'TYPESCRIPT_NOT_STRING', +} + const bundles = {} // by default, we transform JavaScript (including some proposal features), @@ -60,6 +67,16 @@ const defaultOptions = { }, } +const throwError = ({ message, type }) => { + const prefix = 'Error running @cypress/browserify-preprocessor:\n\n' + + const err = new Error(`${prefix}${message}`) + + if (type) err.type = type + + throw err +} + const getBrowserifyOptions = async (entry, userBrowserifyOptions = {}, typescriptPath = null) => { let browserifyOptions = cloneDeep(defaultOptions.browserifyOptions) @@ -83,13 +100,19 @@ const getBrowserifyOptions = async (entry, userBrowserifyOptions = {}, typescrip if (typescriptPath) { if (typeof typescriptPath !== 'string') { - throw new Error(`The 'typescript' option must be a string. You passed: ${typescriptPath}`) + throwError({ + type: errorTypes.TYPESCRIPT_NOT_STRING, + message: `The 'typescript' option must be a string. You passed: ${typescriptPath}`, + }) } const pathExists = await fs.pathExists(typescriptPath) if (!pathExists) { - throw new Error(`The 'typescript' option must be a valid path to your TypeScript installation. We could not find anything at the following path: ${typescriptPath}`) + throwError({ + type: errorTypes.TYPESCRIPT_NONEXISTENT, + message: `The 'typescript' option must be a valid path to your TypeScript installation. We could not find anything at the following path: ${typescriptPath}`, + }) } const transform = browserifyOptions.transform @@ -99,15 +122,15 @@ const getBrowserifyOptions = async (entry, userBrowserifyOptions = {}, typescrip if (hasTsifyTransform || hastsifyPlugin) { const type = hasTsifyTransform ? 'transform' : 'plugin' - throw new Error(`Error running @cypress/browserify-preprocessor: - -It looks like you passed the 'typescript' option and also specified a browserify ${type} for TypeScript. This may cause conflicts. + throwError({ + type: errorTypes.TYPESCRIPT_AND_TSIFY, + message: `It looks like you passed the 'typescript' option and also specified a browserify ${type} for TypeScript. This may cause conflicts. Please do one of the following: 1) Pass in the 'typescript' option and omit the browserify ${type} (Recommmended) -2) Omit the 'typescript' option and continue to use your own browserify ${type} -`) +2) Omit the 'typescript' option and continue to use your own browserify ${type}`, + }) } browserifyOptions.extensions.push('.ts', '.tsx') @@ -263,6 +286,8 @@ const preprocessor = (options = {}) => { // provide a clone of the default options preprocessor.defaultOptions = JSON.parse(JSON.stringify(defaultOptions)) +preprocessor.errorTypes = errorTypes + if (process.env.__TESTING__) { preprocessor.reset = () => { for (let filePath in bundles) { diff --git a/test/unit/index_spec.js b/test/unit/index_spec.js index 5b729f5..dce6acc 100644 --- a/test/unit/index_spec.js +++ b/test/unit/index_spec.js @@ -429,13 +429,19 @@ describe('browserify preprocessor', function () { throw new Error('Should error, should not resolve') } + const verifyErrorIncludesPrefix = (err) => { + expect(err.message).to.include('Error running @cypress/browserify-preprocessor:') + } + it('throws error when typescript path is not a string', function () { this.options.typescript = true return this.run() .then(shouldntResolve) .catch((err) => { - expect(err.message).to.equal(`The 'typescript' option must be a string. You passed: true`) + verifyErrorIncludesPrefix(err) + expect(err.type).to.equal(preprocessor.errorTypes.TYPESCRIPT_NOT_STRING) + expect(err.message).to.include(`The 'typescript' option must be a string. You passed: true`) }) }) @@ -445,7 +451,9 @@ describe('browserify preprocessor', function () { return this.run() .then(shouldntResolve) .catch((err) => { - expect(err.message).to.equal(`The 'typescript' option must be a valid path to your TypeScript installation. We could not find anything at the following path: /nothing/here`) + verifyErrorIncludesPrefix(err) + expect(err.type).to.equal(preprocessor.errorTypes.TYPESCRIPT_NONEXISTENT) + expect(err.message).to.include(`The 'typescript' option must be a valid path to your TypeScript installation. We could not find anything at the following path: /nothing/here`) }) }) @@ -457,7 +465,9 @@ describe('browserify preprocessor', function () { return this.run() .then(shouldntResolve) .catch((err) => { - expect(err.message).to.include('This may cause conflicts') + verifyErrorIncludesPrefix(err) + expect(err.type).to.equal(preprocessor.errorTypes.TYPESCRIPT_AND_TSIFY) + expect(err.message).to.include(`It looks like you passed the 'typescript' option and also specified a browserify plugin for TypeScript. This may cause conflicts`) }) }) @@ -471,7 +481,11 @@ describe('browserify preprocessor', function () { return this.run() .then(shouldntResolve) .catch((err) => { - expect(err.message).to.include('This may cause conflicts') + verifyErrorIncludesPrefix(err) + expect(err.type).to.equal(preprocessor.errorTypes.TYPESCRIPT_AND_TSIFY) + expect(err.message).to.include(`It looks like you passed the 'typescript' option and also specified a browserify transform for TypeScript. This may cause conflicts`) + }) + }) }) }) }) From 36d77a8b7841e25c3e16372c8f4ab7e719d292a4 Mon Sep 17 00:00:00 2001 From: Chris Breiding Date: Thu, 21 May 2020 09:40:23 -0400 Subject: [PATCH 7/8] error if attempting to preprocess typescript file but typescript option is not set --- index.js | 10 ++++++++++ test/unit/index_spec.js | 26 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/index.js b/index.js index 2464300..d122ca2 100644 --- a/index.js +++ b/index.js @@ -14,6 +14,7 @@ const typescriptExtensionRegex = /\.tsx?$/ const errorTypes = { TYPESCRIPT_AND_TSIFY: 'TYPESCRIPT_AND_TSIFY', TYPESCRIPT_NONEXISTENT: 'TYPESCRIPT_NONEXISTENT', + TYPESCRIPT_NOT_CONFIGURED: 'TYPESCRIPT_NOT_CONFIGURED', TYPESCRIPT_NOT_STRING: 'TYPESCRIPT_NOT_STRING', } @@ -193,6 +194,15 @@ const preprocessor = (options = {}) => { const browserifyOptions = await getBrowserifyOptions(filePath, options.browserifyOptions, options.typescript) const watchifyOptions = Object.assign({}, defaultOptions.watchifyOptions, options.watchifyOptions) + if (!options.typescript && typescriptExtensionRegex.test(filePath)) { + throwError({ + type: errorTypes.TYPESCRIPT_NOT_CONFIGURED, + message: `You are attempting to preprocess a TypeScript file, but do not have TypeScript configured. Pass the 'typescript' option to enable TypeScript support. + +The file: ${filePath}`, + }) + } + const bundler = browserify(browserifyOptions) if (file.shouldWatch) { diff --git a/test/unit/index_spec.js b/test/unit/index_spec.js index dce6acc..0d705cf 100644 --- a/test/unit/index_spec.js +++ b/test/unit/index_spec.js @@ -486,6 +486,32 @@ describe('browserify preprocessor', function () { expect(err.message).to.include(`It looks like you passed the 'typescript' option and also specified a browserify transform for TypeScript. This may cause conflicts`) }) }) + + it('throws error when processing .ts file and typescript option is not set', function () { + this.options.typescript = undefined + this.file.filePath = 'path/to/file.ts' + + return this.run() + .then(shouldntResolve) + .catch((err) => { + verifyErrorIncludesPrefix(err) + expect(err.type).to.equal(preprocessor.errorTypes.TYPESCRIPT_NOT_CONFIGURED) + expect(err.message).to.include(`You are attempting to preprocess a TypeScript file, but do not have TypeScript configured. Pass the 'typescript' option to enable TypeScript support`) + expect(err.message).to.include('path/to/file.ts') + }) + }) + + it('throws error when processing .tsx file and typescript option is not set', function () { + this.options.typescript = undefined + this.file.filePath = 'path/to/file.tsx' + + return this.run() + .then(shouldntResolve) + .catch((err) => { + verifyErrorIncludesPrefix(err) + expect(err.type).to.equal(preprocessor.errorTypes.TYPESCRIPT_NOT_CONFIGURED) + expect(err.message).to.include(`You are attempting to preprocess a TypeScript file, but do not have TypeScript configured. Pass the 'typescript' option to enable TypeScript support`) + expect(err.message).to.include('path/to/file.tsx') }) }) }) From 28565c48d05a9ce108f15406d65f70c6a70a002c Mon Sep 17 00:00:00 2001 From: Chris Breiding Date: Thu, 21 May 2020 09:41:10 -0400 Subject: [PATCH 8/8] package-lock.json --- package-lock.json | 54 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 75e5cbb..2bec250 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1344,6 +1344,17 @@ "indent-string": "^3.0.0" } }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, "glob": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", @@ -1932,6 +1943,11 @@ "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=" }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + }, "atob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.1.tgz", @@ -5097,13 +5113,35 @@ } }, "fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.0.tgz", + "integrity": "sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g==", "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" + }, + "jsonfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", + "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^1.0.0" + } + }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==" + } } }, "fs.realpath": { @@ -6495,6 +6533,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, "requires": { "graceful-fs": "^4.1.6" } @@ -13693,7 +13732,8 @@ "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true }, "unset-value": { "version": "1.0.0",