diff --git a/.eslintrc.js b/.eslintrc.js index 122cfe1..5fb0683 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,6 +1,13 @@ module.exports = { + parser: "@typescript-eslint/parser", + plugins: ["@typescript-eslint"], extends: [ "@tophat/eslint-config/base", "@tophat/eslint-config/jest", - ] + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" + ], + settings: { + 'import/resolver': { node: { extensions: ['.ts'] }} + } } diff --git a/.gitignore b/.gitignore index 6704566..54bd47d 100644 --- a/.gitignore +++ b/.gitignore @@ -102,3 +102,5 @@ dist # TernJS port file .tern-port + +*.sw[a-z] diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..86c23c7 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "semi": false +} diff --git a/babel.config.json b/babel.config.json index 87c59e0..6d98969 100644 --- a/babel.config.json +++ b/babel.config.json @@ -1,5 +1,5 @@ { "comments": false, "ignore": ["**/*.test.js"], - "presets": [["@babel/preset-env", { "targets": { "node": 10 } }]] + "presets": ["@babel/preset-typescript", ["@babel/preset-env", { "targets": { "node": 10 } }]] } diff --git a/package.json b/package.json index 7d98493..dba1beb 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "packwatch", "version": "0.0.0", - "main": "dist/index.js", + "main": "dist/cli.js", "description": "📦👀 Keep an eye on your packages' footprint", "keywords": [ "npm", @@ -19,9 +19,9 @@ "author": "Marc Cataford ", "license": "MIT", "files": [ - "dist/**/*.js" + "dist/*.ts" ], - "bin": "./dist/index.js", + "bin": "./dist/cli.js", "repository": { "type": "git", "url": "https://github.com/mcataford/packwatch.git" @@ -32,8 +32,8 @@ ], "scripts": { "prebuild": "rimraf dist", - "build": "babel src -d dist", - "lint": "eslint src *.js", + "build": "babel src/index.ts src/cli.ts -d dist --extensions .ts", + "lint": "eslint src/**/*.ts", "lint:fix": "yarn lint --fix", "test": "jest src", "test:watch": "yarn test --watchAll", @@ -43,7 +43,12 @@ "@babel/cli": "^7.8.4", "@babel/core": "^7.8.6", "@babel/preset-env": "^7.8.7", + "@babel/preset-typescript": "^7.12.7", "@tophat/eslint-config": "^0.7.0", + "@types/jest": "^26.0.19", + "@types/node": "^14.14.13", + "@typescript-eslint/eslint-plugin": "^4.9.1", + "@typescript-eslint/parser": "^4.9.1", "eslint": "^7.0.0", "eslint-config-prettier": "^6.10.0", "eslint-plugin-import": "^2.20.1", @@ -54,6 +59,7 @@ "pre-commit": "^1.2.2", "prettier": "^2.0.5", "rimraf": "^3.0.2", - "semantic-release": "^17.0.4" + "semantic-release": "^17.0.4", + "typescript": "^4.1.3" } } diff --git a/src/__tests__/index.test.ts b/src/__tests__/index.test.ts new file mode 100644 index 0000000..b40c800 --- /dev/null +++ b/src/__tests__/index.test.ts @@ -0,0 +1,262 @@ +import * as childProcess from 'child_process' +import { readFileSync } from 'fs' + +import mockFS from 'mock-fs' + +import runPackwatch from '..' + +jest.mock('child_process') + +function getPackOutput({ packageSize, unpackedSize }) { + return ` +npm notice +npm notice 📦 footprint@0.0.0 +npm notice === Tarball Contents === +npm notice 732B package.json +npm notice 1.8kB dist/helpers.js +npm notice 1.9kB dist/index.js +npm notice === Tarball Details === +npm notice name: footprint +npm notice version: 0.0.0 +npm notice filename: footprint-0.0.0.tgz +npm notice package size: ${packageSize} +npm notice unpacked size: ${unpackedSize} +npm notice shasum: bdf33d471543cd8126338a82a27b16a9010b8dbd +npm notice integrity: sha512-ZZvTg9GVcJw8J[...]bkE0xlqQhlt4Q== +npm notice total files: 3 +npm notice + ` +} + +function getManifest() { + try { + return JSON.parse(readFileSync('.packwatch.json', { encoding: 'utf8' })) + } catch { + /* No manifest */ + } +} + +function setupMockFS({ + hasPackageJSON, + hasManifest, + manifestLimit, + manifestSize, +}) { + const fs = {} + + if (hasPackageJSON) fs['package.json'] = '{}' + + if (hasManifest) + fs['.packwatch.json'] = JSON.stringify({ + unpackedSize: '0.5 B', + limitBytes: manifestLimit, + limit: `${manifestLimit} B`, + packageSize: `${manifestSize} B`, + packageSizeBytes: manifestSize, + }) + mockFS(fs) +} +describe('Packwatch', () => { + let mockLogger + beforeEach(() => { + mockFS({}) + mockLogger = jest.spyOn(global.console, 'log').mockImplementation() + }) + + afterEach(jest.restoreAllMocks) + + afterAll(mockFS.restore) + + it('warns the user and errors if run away from package.json', () => { + mockFS({}) + runPackwatch() + + expect(mockLogger.mock.calls).toHaveLength(1) + expect(mockLogger.mock.calls[0][0]).toMatchInlineSnapshot( + '"🤔 There is no package.json file here. Are you in the root directory of your project?"', + ) + }) + + describe('without manifest', () => { + beforeEach(() => { + setupMockFS({ hasPackageJSON: true }) + }) + + it.each(['1 B', '1.1 B', '1 kB', '1.1 kB', '1 mB', '1.1 mB'])( + 'generates the initial manifest properly (size = %s)', + mockSize => { + jest.spyOn(childProcess, 'spawnSync').mockReturnValue({ + stderr: getPackOutput({ + packageSize: mockSize, + unpackedSize: mockSize, + }), + }) + const returnCode = runPackwatch() + const manifest = getManifest() + expect(returnCode).toEqual(1) + expect(manifest).toEqual({ + limit: mockSize, + packageSize: mockSize, + unpackedSize: mockSize, + }) + }, + ) + + it('outputs expected messaging', () => { + jest.spyOn(childProcess, 'spawnSync').mockReturnValue({ + stderr: getPackOutput({ + packageSize: '1 B', + unpackedSize: '2 B', + }), + }) + + runPackwatch() + + expect(mockLogger.mock.calls).toHaveLength(2) + expect(mockLogger.mock.calls[0][0]).toMatchInlineSnapshot(` + "📝 No Manifest to compare against! Current package stats written to .packwatch.json! + Package size (1 B) adopted as new limit." + `) + expect(mockLogger.mock.calls[1][0]).toMatchInlineSnapshot( + '"❗ It looks like you ran PackWatch without a manifest. To prevent accidental passes in CI or hooks, packwatch will terminate with an error. If you are running packwatch for the first time in your project, this is expected!"', + ) + }) + + it('outputs expected messaging when not updating the manifest', () => { + jest.spyOn(childProcess, 'spawnSync').mockReturnValue({ + stderr: getPackOutput({ + packageSize: '1 B', + unpackedSize: '2 B', + }), + }) + + runPackwatch({ isUpdatingManifest: true }) + + expect(mockLogger.mock.calls).toHaveLength(1) + expect(mockLogger.mock.calls[0][0]).toMatchInlineSnapshot(` + "📝 No Manifest to compare against! Current package stats written to .packwatch.json! + Package size (1 B) adopted as new limit." + `) + }) + }) + + describe('with manifest', () => { + it('messages when the size is equal to the limit', () => { + setupMockFS({ + hasPackageJSON: true, + hasManifest: true, + manifestLimit: 1, + manifestSize: 1, + }) + jest.spyOn(childProcess, 'spawnSync').mockReturnValue({ + stderr: getPackOutput({ + packageSize: '1 B', + unpackedSize: '2 B', + }), + }) + runPackwatch() + expect(mockLogger.mock.calls).toHaveLength(1) + expect(mockLogger.mock.calls[0][0]).toMatchInlineSnapshot( + '"📦 Nothing to report! Your package is the same size as the latest manifest reports! (Limit: 1 B)"', + ) + }) + + it('messages when the size is lower than the limit (no growth)', () => { + setupMockFS({ + hasPackageJSON: true, + hasManifest: true, + manifestLimit: 5, + manifestSize: 1, + }) + jest.spyOn(childProcess, 'spawnSync').mockReturnValue({ + stderr: getPackOutput({ + packageSize: '1 B', + unpackedSize: '2 B', + }), + }) + runPackwatch() + expect(mockLogger.mock.calls).toHaveLength(1) + expect(mockLogger.mock.calls[0][0]).toMatchInlineSnapshot( + '"📦 Nothing to report! Your package is the same size as the latest manifest reports! (Limit: 5 B)"', + ) + }) + it('messages when the size is lower than the limit (growth)', () => { + setupMockFS({ + hasPackageJSON: true, + hasManifest: true, + manifestLimit: 5, + manifestSize: 2, + }) + jest.spyOn(childProcess, 'spawnSync').mockReturnValue({ + stderr: getPackOutput({ + packageSize: '3 B', + unpackedSize: '2 B', + }), + }) + runPackwatch() + expect(mockLogger.mock.calls).toHaveLength(1) + expect(mockLogger.mock.calls[0][0]).toMatchInlineSnapshot( + '"📦 👀 Your package grew! 3 B > 2 B (Limit: 5 B)"', + ) + }) + it('messages when the size is lower than the limit (shrinkage)', () => { + setupMockFS({ + hasPackageJSON: true, + hasManifest: true, + manifestLimit: 5, + manifestSize: 2, + }) + jest.spyOn(childProcess, 'spawnSync').mockReturnValue({ + stderr: getPackOutput({ + packageSize: '1 B', + unpackedSize: '2 B', + }), + }) + runPackwatch() + expect(mockLogger.mock.calls).toHaveLength(1) + expect(mockLogger.mock.calls[0][0]).toMatchInlineSnapshot( + '"📦 💯 Your package shrank! 1 B < 2 B (Limit: 5 B)"', + ) + }) + it('messages when the size exceeds the limit', () => { + setupMockFS({ + hasPackageJSON: true, + hasManifest: true, + manifestLimit: 0.5, + manifestSize: 0.5, + }) + jest.spyOn(childProcess, 'spawnSync').mockReturnValue({ + stderr: getPackOutput({ + packageSize: '1 B', + unpackedSize: '2 B', + }), + }) + runPackwatch() + expect(mockLogger.mock.calls).toHaveLength(1) + expect(mockLogger.mock.calls[0][0]).toMatchInlineSnapshot(` + "🔥🔥📦🔥🔥 Your package exceeds the limit set in .packwatch.json! 1 B > 0.5 B + Either update the limit by using the --update-manifest flag or trim down your packed files!" + `) + }) + + it('messages when updating the manifest', () => { + setupMockFS({ + hasPackageJSON: true, + hasManifest: true, + manifestLimit: 0.5, + manifestSize: 0.5, + }) + jest.spyOn(childProcess, 'spawnSync').mockReturnValue({ + stderr: getPackOutput({ + packageSize: '1 B', + unpackedSize: '2 B', + }), + }) + runPackwatch({ isUpdatingManifest: true }) + expect(mockLogger.mock.calls).toHaveLength(1) + expect(mockLogger.mock.calls[0][0]).toMatchInlineSnapshot( + '"📝 Updated the manifest! Package size: 1 B, Limit: 1 B"', + ) + }) + }) +}) diff --git a/src/cli.ts b/src/cli.ts new file mode 100644 index 0000000..6742f26 --- /dev/null +++ b/src/cli.ts @@ -0,0 +1,7 @@ +#!/usr/bin/env node + +import runPackwatch from '.' + +const isUpdatingManifest = process.argv.includes('--update-manifest') +const processExit = runPackwatch({ isUpdatingManifest }) +process.exit(processExit) diff --git a/src/helpers.js b/src/helpers.js deleted file mode 100644 index 03f1399..0000000 --- a/src/helpers.js +++ /dev/null @@ -1,73 +0,0 @@ -const { spawnSync } = require('child_process') -const { readFileSync, writeFileSync } = require('fs') - -const PACKAGE_SIZE_PATT = /package size:\s+([0-9]+\.?[0-9]*\s+[A-Za-z]+)/g -const UNPACKED_SIZE_PATT = /unpacked size:\s+([0-9]+\.?[0-9]*\s+[A-Za-z]+)/g -const SIZE_SUFFIX_PATT = /([A-Za-z]+)/ -const SIZE_MAGNITUDE_PATT = /([0-9]+\.?[0-9]*)/ -const MANIFEST_FILENAME = '.packwatch.json' - -const FS_OPTIONS = { encoding: 'utf-8' } - -function convertSizeToBytes(sizeString) { - const sizeSuffix = SIZE_SUFFIX_PATT.exec(sizeString)[1] - const sizeMagnitude = SIZE_MAGNITUDE_PATT.exec(sizeString)[1] - - let multiplier = 1 - - if (sizeSuffix === 'kB') multiplier = 1000 - else if (sizeSuffix === 'mB') { - multiplier = 1000000 - } - - return multiplier * parseFloat(sizeMagnitude) -} - -function getCurrentPackageStats() { - const { stderr } = spawnSync('npm', ['pack', '--dry-run'], FS_OPTIONS) - const packageSize = PACKAGE_SIZE_PATT.exec(stderr)[1] - const unpackedSize = UNPACKED_SIZE_PATT.exec(stderr)[1] - - return { - packageSize, - unpackedSize, - packageSizeBytes: convertSizeToBytes(packageSize), - unpackedSizeBytes: convertSizeToBytes(unpackedSize), - } -} - -function getPreviousPackageStats() { - try { - const currentManifest = readFileSync(MANIFEST_FILENAME, FS_OPTIONS) - const parsedManifest = JSON.parse(currentManifest) - return { - ...parsedManifest, - packageSizeBytes: convertSizeToBytes(parsedManifest.packageSize), - unpackedSizeBytes: convertSizeToBytes(parsedManifest.unpackedSize), - limitBytes: convertSizeToBytes(parsedManifest.limit), - } - } catch (e) { - return {} - } -} - -function createOrUpdateManifest({ previous, current, updateLimit = false }) { - const { limit } = previous || {} - const { packageSize, unpackedSize } = current - - const newManifest = { - limit: updateLimit ? packageSize : limit || packageSize, - packageSize: packageSize, - unpackedSize: unpackedSize, - } - - writeFileSync(MANIFEST_FILENAME, JSON.stringify(newManifest)) -} - -module.exports = { - createOrUpdateManifest, - getPreviousPackageStats, - getCurrentPackageStats, - convertSizeToBytes, - MANIFEST_FILENAME, -} diff --git a/src/index.d.ts b/src/index.d.ts new file mode 100644 index 0000000..c0a52f0 --- /dev/null +++ b/src/index.d.ts @@ -0,0 +1,8 @@ +export type Report = { + packageSize: string + unpackedSize?: string + packageSizeBytes?: number + unpackedSizeBytes?: number + limit?: string + limitBytes?: number +} diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 4d05c07..0000000 --- a/src/index.js +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/env node - -const { existsSync } = require('fs') - -const { - MANIFEST_FILENAME, - getCurrentPackageStats, - getPreviousPackageStats, - createOrUpdateManifest, -} = require('./helpers') - -if (!existsSync('package.json')) { - console.log( - '🤔 There is no package.json file here. Are you in the root directory of your project?', - ) - process.exit(1) -} - -const isUpdatingManifest = process.argv.includes('--update-manifest') - -const currentStats = getCurrentPackageStats() - -/* - * If there is no manifest file yet, we can use the current package stats as - * a base to build one. The current package size becomes the limit. - */ - -if (!existsSync(MANIFEST_FILENAME)) { - createOrUpdateManifest({ current: currentStats }) - console.log( - `📝 No Manifest to compare against! Current package stats written to ${MANIFEST_FILENAME}!`, - ) - console.log( - `Package size (${currentStats.packageSize}) adopted as new limit.`, - ) - - if (!isUpdatingManifest) { - console.log( - '❗ It looks like you ran PackWatch without a manifest. To prevent accidental passes in CI or hooks, packwatch will terminate with an error. If you are running packwatch for the first time in your project, this is expected!', - ) - } - // If the update flag wasn't specified, exit with a non-zero code so we - // don't "accidentally" pass CI builds if the manifest didn't exist - process.exit(isUpdatingManifest ? 0 : 1) -} - -const previousStats = getPreviousPackageStats() -const { packageSizeBytes, packageSize } = currentStats -const { - packageSize: previousSize, - packageSizeBytes: previousSizeBytes, - limit, - limitBytes, -} = previousStats -const hasExceededLimit = packageSizeBytes > limitBytes - -/* - * If we are updating the manifest, we can write right away and terminate. - */ - -if (isUpdatingManifest) { - createOrUpdateManifest({ - previous: previousStats, - current: currentStats, - updateLimit: true, - }) - console.log( - `📝 Updated the manifest! Package size: ${packageSize}, Limit: ${packageSize}`, - ) - process.exit(0) -} - -/* - * If there is a manifest file and the current package busts its limit - * we signal it and terminate with an error. - */ - -if (hasExceededLimit) { - console.log( - `🔥🔥📦🔥🔥 Your package exceeds the limit set in ${MANIFEST_FILENAME}! ${packageSize} > ${limit}`, - ) - console.log( - 'Either update the limit by using the --update-manifest flag or trim down your packed files!', - ) - process.exit(1) -} - -/* - * If there is a manifest file and the limit is not busted, we give - * the user some feedback on how the current package compares with - * the previous one. - */ - -if (packageSizeBytes > previousSizeBytes) { - console.log( - `📦 👀 Your package grew! ${packageSize} > ${previousSize} (Limit: ${limit})`, - ) -} else if (packageSizeBytes < previousSizeBytes) { - console.log( - `📦 💯 Your package shrank! ${packageSize} < ${previousSize} (Limit: ${limit})`, - ) -} else { - console.log( - `📦 Nothing to report! Your package is the same size as the latest manifest reports! (Limit: ${limit})`, - ) -} diff --git a/src/index.test.js b/src/index.test.js deleted file mode 100644 index ae0a9aa..0000000 --- a/src/index.test.js +++ /dev/null @@ -1,177 +0,0 @@ -const childProcess = require('child_process') -const { readFileSync } = require('fs') - -const mockFS = require('mock-fs') - -jest.mock('child_process') -childProcess.spawnSync = jest.fn(() => ({ stderr: mockPackOutput })) - -const { - MANIFEST_FILENAME, - convertSizeToBytes, - getCurrentPackageStats, - getPreviousPackageStats, - createOrUpdateManifest, -} = require('./helpers') - -const mockPackageSize = '1.1 kB' -const mockUnpackedSize = '9000 kB' - -const mockPackOutput = ` -npm notice -npm notice 📦 footprint@0.0.0 -npm notice === Tarball Contents === -npm notice 732B package.json -npm notice 1.8kB dist/helpers.js -npm notice 1.9kB dist/index.js -npm notice === Tarball Details === -npm notice name: footprint -npm notice version: 0.0.0 -npm notice filename: footprint-0.0.0.tgz -npm notice package size: ${mockPackageSize} -npm notice unpacked size: ${mockUnpackedSize} -npm notice shasum: bdf33d471543cd8126338a82a27b16a9010b8dbd -npm notice integrity: sha512-ZZvTg9GVcJw8J[...]bkE0xlqQhlt4Q== -npm notice total files: 3 -npm notice - ` -describe('Helpers', () => { - beforeEach(() => { - mockFS.restore() - jest.restoreAllMocks() - }) - - afterAll(mockFS.restore) - - describe('Size string conversion', () => { - it.each` - sizeString | expectedValue - ${'1 B'} | ${1} - ${'1.1 B'} | ${1.1} - ${'1 kB'} | ${1000} - ${'1.1kB'} | ${1100} - ${'1 mB'} | ${1000000} - ${'1.1 mB'} | ${1100000} - `( - 'converts $sizeString properly to $expectedValue bytes', - ({ sizeString, expectedValue }) => { - expect(convertSizeToBytes(sizeString)).toEqual(expectedValue) - }, - ) - }) - - describe('Current package statistics', () => { - it('constructs the current package report properly', () => { - const packageSizeBytes = 1100 - const unpackedSizeBytes = 9000000 - - expect(getCurrentPackageStats()).toEqual({ - packageSize: mockPackageSize, - packageSizeBytes, - unpackedSize: mockUnpackedSize, - unpackedSizeBytes, - }) - }) - }) - - describe('Previous package statistics', () => { - it('constructs the previous package report properly', () => { - const packageSize = '0.9 kB' - const packageSizeBytes = 900 - const unpackedSize = '90 kB' - const unpackedSizeBytes = 90000 - const limit = '1 kB' - const limitBytes = 1000 - const mockReport = { packageSize, unpackedSize, limit } - mockFS({ [MANIFEST_FILENAME]: JSON.stringify(mockReport) }) - - expect(getPreviousPackageStats()).toEqual({ - packageSize, - packageSizeBytes, - unpackedSize, - unpackedSizeBytes, - limit, - limitBytes, - }) - }) - - it('returns an empty manifest if it fails to reads the manifest file', () => { - mockFS({ - [MANIFEST_FILENAME]: 'not valid JSON', - }) - - expect(getPreviousPackageStats()).toEqual({}) - }) - }) - - describe('Creating or updating the manifest', () => { - const currentStats = { - packageSize: '1 kB', - unpackedSize: '10 kB', - } - - const previousManifest = { - limit: '2 kB', - packageSize: '1.5 kB', - } - it('creates a anifest from the current data if no previous data is provided', () => { - mockFS({}) - - createOrUpdateManifest({ current: currentStats }) - - const writtenManifest = readFileSync(MANIFEST_FILENAME, { - encoding: 'utf-8', - }) - - expect(JSON.parse(writtenManifest)).toEqual({ - packageSize: currentStats.packageSize, - unpackedSize: currentStats.unpackedSize, - limit: currentStats.packageSize, - }) - }) - - it('updates the previous manifest sizes if previous data exists', () => { - mockFS({ - [MANIFEST_FILENAME]: JSON.stringify(previousManifest), - }) - - createOrUpdateManifest({ - current: currentStats, - previous: previousManifest, - updateLimit: false, - }) - - const writtenManifest = readFileSync(MANIFEST_FILENAME, { - encoding: 'utf-8', - }) - - expect(JSON.parse(writtenManifest)).toEqual({ - packageSize: currentStats.packageSize, - unpackedSize: currentStats.unpackedSize, - limit: previousManifest.limit, - }) - }) - - it('updates the previous manifest sizes and limit if previous data exists and updateLimit is set', () => { - mockFS({ - [MANIFEST_FILENAME]: JSON.stringify(previousManifest), - }) - - createOrUpdateManifest({ - current: currentStats, - previous: previousManifest, - updateLimit: true, - }) - - const writtenManifest = readFileSync(MANIFEST_FILENAME, { - encoding: 'utf-8', - }) - - expect(JSON.parse(writtenManifest)).toEqual({ - packageSize: currentStats.packageSize, - unpackedSize: currentStats.unpackedSize, - limit: currentStats.packageSize, - }) - }) - }) -}) diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..fb7d0ed --- /dev/null +++ b/src/index.ts @@ -0,0 +1,177 @@ +import { spawnSync } from 'child_process' +import { existsSync, readFileSync, writeFileSync } from 'fs' + +import { Report } from './index.d' + +const PACKAGE_SIZE_PATT = /package size:\s*([0-9]+\.?[0-9]*\s+[A-Za-z]{1,2})/ +const UNPACKED_SIZE_PATT = /unpacked size:\s*([0-9]+\.?[0-9]*\s+[A-Za-z]{1,2})/ +const SIZE_SUFFIX_PATT = /([A-Za-z]+)/ +const SIZE_MAGNITUDE_PATT = /([0-9]+\.?[0-9]*)/ + +const MANIFEST_FILENAME = '.packwatch.json' + +function convertSizeToBytes(sizeString: string): number { + const sizeSuffix = SIZE_SUFFIX_PATT.exec(sizeString)[1] + const sizeMagnitude = SIZE_MAGNITUDE_PATT.exec(sizeString)[1] + + let multiplier = 1 + + if (sizeSuffix === 'kB') multiplier = 1000 + else if (sizeSuffix === 'mB') { + multiplier = 1000000 + } + + return multiplier * parseFloat(sizeMagnitude) +} + +function getCurrentPackageStats(): Report { + const { stderr } = spawnSync('npm', ['pack', '--dry-run'], { + encoding: 'utf-8', + }) + const stderrString = String(stderr) + const packageSize = PACKAGE_SIZE_PATT.exec(stderrString)[1] + const unpackedSize = UNPACKED_SIZE_PATT.exec(stderrString)[1] + + return { + packageSize, + unpackedSize, + packageSizeBytes: convertSizeToBytes(packageSize), + unpackedSizeBytes: convertSizeToBytes(unpackedSize), + } +} + +function getPreviousPackageStats(): Report | null { + try { + const currentManifest = readFileSync(MANIFEST_FILENAME, { + encoding: 'utf-8', + }) + const parsedManifest = JSON.parse(currentManifest) + return { + ...parsedManifest, + packageSizeBytes: convertSizeToBytes(parsedManifest.packageSize), + unpackedSizeBytes: convertSizeToBytes(parsedManifest.unpackedSize), + limitBytes: convertSizeToBytes(parsedManifest.limit), + } + } catch { + /* No manifest */ + } +} + +function createOrUpdateManifest({ + previous, + current, + updateLimit = false, +}: { + previous?: Report + current: Report + updateLimit?: boolean +}): void { + const { limit } = previous || {} + const { packageSize, unpackedSize } = current + + const newManifest = { + limit: updateLimit ? packageSize : limit || packageSize, + packageSize: packageSize, + unpackedSize: unpackedSize, + } + + writeFileSync(MANIFEST_FILENAME, JSON.stringify(newManifest)) +} +export default function run( + { + manifestFn = MANIFEST_FILENAME, + isUpdatingManifest, + }: { manifestFn?: string; isUpdatingManifest?: boolean } = { + manifestFn: MANIFEST_FILENAME, + isUpdatingManifest: false, + }, +): number { + if (!existsSync('package.json')) { + console.log( + '🤔 There is no package.json file here. Are you in the root directory of your project?', + ) + return 1 + } + + const currentStats = getCurrentPackageStats() + + /* + * If there is no manifest file yet, we can use the current package stats as + * a base to build one. The current package size becomes the limit. + */ + + if (!existsSync(manifestFn)) { + createOrUpdateManifest({ current: currentStats }) + console.log( + `📝 No Manifest to compare against! Current package stats written to ${MANIFEST_FILENAME}!\nPackage size (${currentStats.packageSize}) adopted as new limit.`, + ) + + if (!isUpdatingManifest) { + console.log( + '❗ It looks like you ran PackWatch without a manifest. To prevent accidental passes in CI or hooks, packwatch will terminate with an error. If you are running packwatch for the first time in your project, this is expected!', + ) + } + // If the update flag wasn't specified, exit with a non-zero code so we + // don't "accidentally" pass CI builds if the manifest didn't exist + return isUpdatingManifest ? 0 : 1 + } + + const previousStats = getPreviousPackageStats() + const { packageSizeBytes, packageSize } = currentStats + const { + packageSize: previousSize, + packageSizeBytes: previousSizeBytes, + limit, + limitBytes, + } = previousStats + const hasExceededLimit = packageSizeBytes > limitBytes + + /* + * If we are updating the manifest, we can write right away and terminate. + */ + + if (isUpdatingManifest) { + createOrUpdateManifest({ + previous: previousStats, + current: currentStats, + updateLimit: true, + }) + console.log( + `📝 Updated the manifest! Package size: ${packageSize}, Limit: ${packageSize}`, + ) + return 0 + } + + /* + * If there is a manifest file and the current package busts its limit + * we signal it and terminate with an error. + */ + + if (hasExceededLimit) { + console.log( + `🔥🔥📦🔥🔥 Your package exceeds the limit set in ${MANIFEST_FILENAME}! ${packageSize} > ${limit}\nEither update the limit by using the --update-manifest flag or trim down your packed files!`, + ) + return 1 + } + + /* + * If there is a manifest file and the limit is not busted, we give + * the user some feedback on how the current package compares with + * the previous one. + */ + + if (packageSizeBytes > previousSizeBytes) { + console.log( + `📦 👀 Your package grew! ${packageSize} > ${previousSize} (Limit: ${limit})`, + ) + } else if (packageSizeBytes < previousSizeBytes) { + console.log( + `📦 💯 Your package shrank! ${packageSize} < ${previousSize} (Limit: ${limit})`, + ) + } else { + console.log( + `📦 Nothing to report! Your package is the same size as the latest manifest reports! (Limit: ${limit})`, + ) + } + return 0 +} diff --git a/yarn.lock b/yarn.lock index b3ea588..4c97e75 100644 --- a/yarn.lock +++ b/yarn.lock @@ -103,6 +103,17 @@ "@babel/helper-replace-supers" "^7.10.4" "@babel/helper-split-export-declaration" "^7.10.4" +"@babel/helper-create-class-features-plugin@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz#3c45998f431edd4a9214c5f1d3ad1448a6137f6e" + integrity sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-member-expression-to-functions" "^7.12.1" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/helper-replace-supers" "^7.12.1" + "@babel/helper-split-export-declaration" "^7.10.4" + "@babel/helper-create-regexp-features-plugin@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz#fdd60d88524659a0b6959c0579925e425714f3b8" @@ -286,6 +297,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== +"@babel/helper-validator-option@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.1.tgz#175567380c3e77d60ff98a54bb015fe78f2178d9" + integrity sha512-YpJabsXlJVWP0USHjnC/AQDTLlZERbON577YUVO/wLpqyj6HAtVYnWaQaN0iUN+1/tWn3c+uKKXjRut5115Y2A== + "@babel/helper-wrap-function@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz#8a6f701eab0ff39f765b5a1cfef409990e624b87" @@ -517,6 +533,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-syntax-typescript@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.12.1.tgz#460ba9d77077653803c3dd2e673f76d66b4029e5" + integrity sha512-UZNEcCY+4Dp9yYRCAHrHDU+9ZXLYaY9MgBXSRLkB9WjYFRR6quJBumfVrEkUxrePPBwFcpWfNKXqVRQQtm7mMA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-arrow-functions@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz#e22960d77e697c74f41c501d44d73dbf8a6a64cd" @@ -754,6 +777,15 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-typescript@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.12.1.tgz#d92cc0af504d510e26a754a7dbc2e5c8cd9c7ab4" + integrity sha512-VrsBByqAIntM+EYMqSm59SiMEf7qkmI9dqMt6RbD/wlwueWmYcI0FFK5Fj47pP6DRZm+3teXjosKlwcZJ5lIMw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-typescript" "^7.12.1" + "@babel/plugin-transform-unicode-escapes@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz#feae523391c7651ddac115dae0a9d06857892007" @@ -854,6 +886,15 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" +"@babel/preset-typescript@^7.12.7": + version "7.12.7" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.12.7.tgz#fc7df8199d6aae747896f1e6c61fc872056632a3" + integrity sha512-nOoIqIqBmHBSEgBXWR4Dv/XBehtIFcw9PqZw6rFYuKrzsZmOQm3PR5siLBnKZFEsDb03IegG8nSjU/iXXXYRmw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-validator-option" "^7.12.1" + "@babel/plugin-transform-typescript" "^7.12.1" + "@babel/runtime@^7.6.3": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.4.tgz#d79f5a2040f7caa24d53e563aad49cbc05581308" @@ -1109,6 +1150,17 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" +"@jest/types@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" + integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + "@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents": version "2.1.8-no-fsevents" resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.tgz#da7c3996b8e6e19ebd14d82eaced2313e7769f9b" @@ -1419,6 +1471,21 @@ "@types/istanbul-lib-coverage" "*" "@types/istanbul-lib-report" "*" +"@types/istanbul-reports@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821" + integrity sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/jest@^26.0.19": + version "26.0.19" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.19.tgz#e6fa1e3def5842ec85045bd5210e9bb8289de790" + integrity sha512-jqHoirTG61fee6v6rwbnEuKhpSKih0tuhqeFbCmMmErhtu3BYlOZaXWjffgOstMM4S/3iQD31lI5bGLTrs97yQ== + dependencies: + jest-diff "^26.0.0" + pretty-format "^26.0.0" + "@types/json-schema@^7.0.3": version "7.0.4" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" @@ -1439,6 +1506,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.7.tgz#1628e6461ba8cc9b53196dfeaeec7b07fa6eea99" integrity sha512-Uo4chgKbnPNlxQwoFmYIwctkQVkMMmsAoGGU4JKwLuvBefF0pCq4FybNSnfkfRCpC7ZW7kttcC/TrRtAJsvGtg== +"@types/node@^14.14.13": + version "14.14.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.13.tgz#9e425079799322113ae8477297ae6ef51b8e0cdf" + integrity sha512-vbxr0VZ8exFMMAjCW8rJwaya0dMCDyYW2ZRdTyjtrCvJoENMpdUHOT/eTzvgyA5ZnqRZ/sI0NwqAxNHKYokLJQ== + "@types/normalize-package-data@^2.4.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" @@ -1476,6 +1548,31 @@ dependencies: "@types/yargs-parser" "*" +"@typescript-eslint/eslint-plugin@^4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.9.1.tgz#66758cbe129b965fe9c63b04b405d0cf5280868b" + integrity sha512-QRLDSvIPeI1pz5tVuurD+cStNR4sle4avtHhxA+2uyixWGFjKzJ+EaFVRW6dA/jOgjV5DTAjOxboQkRDE8cRlQ== + dependencies: + "@typescript-eslint/experimental-utils" "4.9.1" + "@typescript-eslint/scope-manager" "4.9.1" + debug "^4.1.1" + functional-red-black-tree "^1.0.1" + regexpp "^3.0.0" + semver "^7.3.2" + tsutils "^3.17.1" + +"@typescript-eslint/experimental-utils@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.9.1.tgz#86633e8395191d65786a808dc3df030a55267ae2" + integrity sha512-c3k/xJqk0exLFs+cWSJxIjqLYwdHCuLWhnpnikmPQD2+NGAx9KjLYlBDcSI81EArh9FDYSL6dslAUSwILeWOxg== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/scope-manager" "4.9.1" + "@typescript-eslint/types" "4.9.1" + "@typescript-eslint/typescript-estree" "4.9.1" + eslint-scope "^5.0.0" + eslint-utils "^2.0.0" + "@typescript-eslint/experimental-utils@^2.5.0": version "2.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.21.0.tgz#71de390a3ec00b280b69138d80733406e6e86bfa" @@ -1485,6 +1582,29 @@ "@typescript-eslint/typescript-estree" "2.21.0" eslint-scope "^5.0.0" +"@typescript-eslint/parser@^4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.9.1.tgz#2d74c4db5dd5117379a9659081a4d1ec02629055" + integrity sha512-Gv2VpqiomvQ2v4UL+dXlQcZ8zCX4eTkoIW+1aGVWT6yTO+6jbxsw7yQl2z2pPl/4B9qa5JXeIbhJpONKjXIy3g== + dependencies: + "@typescript-eslint/scope-manager" "4.9.1" + "@typescript-eslint/types" "4.9.1" + "@typescript-eslint/typescript-estree" "4.9.1" + debug "^4.1.1" + +"@typescript-eslint/scope-manager@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.9.1.tgz#cc2fde310b3f3deafe8436a924e784eaab265103" + integrity sha512-sa4L9yUfD/1sg9Kl8OxPxvpUcqxKXRjBeZxBuZSSV1v13hjfEJkn84n0An2hN8oLQ1PmEl2uA6FkI07idXeFgQ== + dependencies: + "@typescript-eslint/types" "4.9.1" + "@typescript-eslint/visitor-keys" "4.9.1" + +"@typescript-eslint/types@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.9.1.tgz#a1a7dd80e4e5ac2c593bc458d75dd1edaf77faa2" + integrity sha512-fjkT+tXR13ks6Le7JiEdagnwEFc49IkOyys7ueWQ4O8k4quKPwPJudrwlVOJCUQhXo45PrfIvIarcrEjFTNwUA== + "@typescript-eslint/typescript-estree@2.21.0": version "2.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.21.0.tgz#7e4be29f2e338195a2e8c818949ed0ff727cc943" @@ -1498,6 +1618,28 @@ semver "^6.3.0" tsutils "^3.17.1" +"@typescript-eslint/typescript-estree@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.9.1.tgz#6e5b86ff5a5f66809e1f347469fadeec69ac50bf" + integrity sha512-bzP8vqwX6Vgmvs81bPtCkLtM/Skh36NE6unu6tsDeU/ZFoYthlTXbBmpIrvosgiDKlWTfb2ZpPELHH89aQjeQw== + dependencies: + "@typescript-eslint/types" "4.9.1" + "@typescript-eslint/visitor-keys" "4.9.1" + debug "^4.1.1" + globby "^11.0.1" + is-glob "^4.0.1" + lodash "^4.17.15" + semver "^7.3.2" + tsutils "^3.17.1" + +"@typescript-eslint/visitor-keys@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.9.1.tgz#d76374a58c4ead9e92b454d186fea63487b25ae1" + integrity sha512-9gspzc6UqLQHd7lXQS7oWs+hrYggspv/rk6zzEMhCbYwPE/sF7oxo7GAjkS35Tdlt7wguIG+ViWCPtVZHz/ybQ== + dependencies: + "@typescript-eslint/types" "4.9.1" + eslint-visitor-keys "^2.0.0" + JSONStream@^1.0.4, JSONStream@^1.3.4, JSONStream@^1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -2637,7 +2779,7 @@ debug@^3.1.0: dependencies: ms "^2.1.1" -debuglog@*, debuglog@^1.0.1: +debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= @@ -2759,6 +2901,11 @@ diff-sequences@^26.0.0: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.0.0.tgz#0760059a5c287637b842bd7085311db7060e88a6" integrity sha512-JC/eHYEC3aSS0vZGjuoc4vHA0yAQTzhQQldXMeMF+JlxLGJlCO38Gma82NV9gk1jGFz8mDzUMeaKXvjRRdJ2dg== +diff-sequences@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" + integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== + dir-glob@^3.0.0, dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -3056,7 +3203,7 @@ eslint-scope@^5.0.0, eslint-scope@^5.1.0: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-utils@^2.1.0: +eslint-utils@^2.0.0, eslint-utils@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== @@ -3068,6 +3215,11 @@ eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== +eslint-visitor-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" + integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== + eslint@^7.0.0: version "7.6.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.6.0.tgz#522d67cfaea09724d96949c70e7a0550614d64d6" @@ -3689,6 +3841,18 @@ globby@^11.0.0: merge2 "^1.3.0" slash "^3.0.0" +globby@^11.0.1: + version "11.0.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357" + integrity sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.1.1" + ignore "^5.1.4" + merge2 "^1.3.0" + slash "^3.0.0" + got@^6.7.1: version "6.7.1" resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" @@ -3955,7 +4119,7 @@ import-local@^3.0.2: pkg-dir "^4.2.0" resolve-cwd "^3.0.0" -imurmurhash@*, imurmurhash@^0.1.4: +imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= @@ -4489,6 +4653,16 @@ jest-config@^26.2.2: micromatch "^4.0.2" pretty-format "^26.2.0" +jest-diff@^26.0.0: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" + integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== + dependencies: + chalk "^4.0.0" + diff-sequences "^26.6.2" + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + jest-diff@^26.2.0: version "26.2.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.2.0.tgz#dee62c771adbb23ae585f3f1bd289a6e8ef4f298" @@ -4547,6 +4721,11 @@ jest-get-type@^26.0.0: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.0.0.tgz#381e986a718998dbfafcd5ec05934be538db4039" integrity sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg== +jest-get-type@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" + integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== + jest-haste-map@^26.2.2: version "26.2.2" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.2.2.tgz#6d4267b1903854bfdf6a871419f35a82f03ae71e" @@ -5209,11 +5388,6 @@ lockfile@^1.0.4: dependencies: signal-exit "^3.0.2" -lodash._baseindexof@*: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c" - integrity sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw= - lodash._baseuniq@~4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8" @@ -5222,33 +5396,11 @@ lodash._baseuniq@~4.6.0: lodash._createset "~4.0.0" lodash._root "~3.0.0" -lodash._bindcallback@*: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" - integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4= - -lodash._cacheindexof@*: - version "3.0.2" - resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92" - integrity sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI= - -lodash._createcache@*: - version "3.1.2" - resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093" - integrity sha1-VtagZAF2JeeevKa4AY4XRAvc8JM= - dependencies: - lodash._getnative "^3.0.0" - lodash._createset@~4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26" integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY= -lodash._getnative@*, lodash._getnative@^3.0.0: - version "3.9.1" - resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" - integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= - lodash._root@~3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" @@ -5284,11 +5436,6 @@ lodash.isstring@^4.0.1: resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= -lodash.restparam@*: - version "3.6.1" - resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" - integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU= - lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" @@ -5949,7 +6096,6 @@ npm@^6.10.3: cmd-shim "^3.0.3" columnify "~1.5.4" config-chain "^1.1.12" - debuglog "*" detect-indent "~5.0.0" detect-newline "^2.1.0" dezalgo "~1.0.3" @@ -5964,7 +6110,6 @@ npm@^6.10.3: has-unicode "~2.0.1" hosted-git-info "^2.8.8" iferr "^1.0.2" - imurmurhash "*" infer-owner "^1.0.4" inflight "~1.0.6" inherits "^2.0.4" @@ -5983,14 +6128,8 @@ npm@^6.10.3: libnpx "^10.2.2" lock-verify "^2.1.0" lockfile "^1.0.4" - lodash._baseindexof "*" lodash._baseuniq "~4.6.0" - lodash._bindcallback "*" - lodash._cacheindexof "*" - lodash._createcache "*" - lodash._getnative "*" lodash.clonedeep "~4.5.0" - lodash.restparam "*" lodash.union "~4.6.0" lodash.uniq "~4.5.0" lodash.without "~4.4.0" @@ -6606,6 +6745,16 @@ prettier@^2.0.5: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.5.tgz#d6d56282455243f2f92cc1716692c08aa31522d4" integrity sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg== +pretty-format@^26.0.0, pretty-format@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" + integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== + dependencies: + "@jest/types" "^26.6.2" + ansi-regex "^5.0.0" + ansi-styles "^4.0.0" + react-is "^17.0.1" + pretty-format@^26.2.0: version "26.2.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.2.0.tgz#83ecc8d7de676ff224225055e72bd64821cec4f1" @@ -6765,6 +6914,11 @@ react-is@^16.12.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.0.tgz#0f37c3613c34fe6b37cd7f763a0d6293ab15c527" integrity sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA== +react-is@^17.0.1: + version "17.0.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339" + integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA== + read-cmd-shim@^1.0.1, read-cmd-shim@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-1.0.5.tgz#87e43eba50098ba5a32d0ceb583ab8e43b961c16" @@ -6978,7 +7132,7 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexpp@^3.1.0: +regexpp@^3.0.0, regexpp@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== @@ -8111,6 +8265,11 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= +typescript@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7" + integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== + uglify-js@^3.1.4: version "3.8.0" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.8.0.tgz#f3541ae97b2f048d7e7e3aa4f39fd8a1f5d7a805"