├── .husky ├── .gitignore ├── pre-commit └── prepare-commit-msg ├── .tool-versions ├── .eslintignore ├── test ├── example │ ├── .gitignore │ ├── src │ │ └── index.js │ ├── dist │ │ └── index.js │ ├── package.json │ └── package-lock.json ├── tsconfig.json ├── helpers │ └── init.js ├── mockfs │ ├── unzip.ts │ ├── test.ts │ └── mockfs.factory.ts └── commands │ └── run │ ├── string-stream.test.ts │ ├── zip.test.ts │ └── index.test.ts ├── bin ├── dev.cmd ├── run.cmd ├── run └── dev ├── src ├── index.ts ├── common │ ├── json-result.ts │ ├── output-info.ts │ ├── custom-error-options.ts │ ├── json-error.ts │ ├── custom-error.ts │ ├── transformers │ │ ├── file-in-memory.transformer.ts │ │ ├── memory-stream.transformer.ts │ │ └── uglify-js.transformer.ts │ ├── fs.ts │ ├── string-stream.ts │ ├── string-to-uint.ts │ ├── custom-command.ts │ ├── extensions.ts │ ├── headless.ts │ └── zip.ts └── commands │ └── run │ └── index.ts ├── benchmark ├── .gitignore ├── package.json ├── node-modules-packer.js ├── utils.js ├── suites.js ├── npm-prune.js ├── README.md ├── suite │ └── package.json └── package-lock.json ├── tsconfig.build.json ├── .gitattributes ├── .prettierrc ├── .mocharc.json ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── workflows │ ├── pr.yml │ ├── release.yml │ └── codeql-analysis.yml └── PULL_REQUEST_TEMPLATE.md ├── LICENSE ├── .releaserc.json ├── .gitignore ├── CHANGELOG.md ├── package.json ├── .eslintrc ├── tsconfig.json └── README.md /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | nodejs lts-fermium 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | src/types/global.d.ts 2 | -------------------------------------------------------------------------------- /test/example/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /bin/dev.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | node "%~dp0\dev" %* -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { run } from '@oclif/core'; 2 | -------------------------------------------------------------------------------- /bin/run.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | node "%~dp0\run" %* 4 | -------------------------------------------------------------------------------- /test/example/src/index.js: -------------------------------------------------------------------------------- 1 | console.log('Hello world.'); 2 | -------------------------------------------------------------------------------- /test/example/dist/index.js: -------------------------------------------------------------------------------- 1 | console.log('Hello world.'); 2 | -------------------------------------------------------------------------------- /benchmark/.gitignore: -------------------------------------------------------------------------------- 1 | prune.zip 2 | lib.zip 3 | suite/node_modules 4 | node_modules 5 | *.zip 6 | -------------------------------------------------------------------------------- /src/common/json-result.ts: -------------------------------------------------------------------------------- 1 | export default interface JsonResult { 2 | status: string; 3 | } 4 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src/**/*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the repository to show as TypeScript rather than JS in GitHub 2 | *.js linguist-detectable=false -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npm run build 5 | npm run test 6 | npx lint-staged 7 | -------------------------------------------------------------------------------- /src/common/output-info.ts: -------------------------------------------------------------------------------- 1 | export interface OutputInfo { 2 | file: string; 3 | path: string; 4 | size: number; 5 | } 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all", 4 | "arrowParens": "avoid", 5 | "printWidth": 80 6 | } 7 | -------------------------------------------------------------------------------- /src/common/custom-error-options.ts: -------------------------------------------------------------------------------- 1 | export interface CustomErrorOptions { 2 | code?: string; 3 | suggestions?: string[]; 4 | } 5 | -------------------------------------------------------------------------------- /.husky/prepare-commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | exec use ts-node and dev plugins 9 | process.env.NODE_ENV = 'development' 10 | 11 | require('ts-node').register({project}) 12 | 13 | // In dev mode, always show stack traces 14 | oclif.settings.debug = true; 15 | 16 | // Start the CLI 17 | oclif.run().then(oclif.flush).catch(oclif.Errors.handle) 18 | -------------------------------------------------------------------------------- /src/common/custom-error.ts: -------------------------------------------------------------------------------- 1 | import { CustomErrorOptions } from './custom-error-options'; 2 | 3 | export default class CustomError extends Error { 4 | constructor(message: string, options?: CustomErrorOptions) { 5 | super(message); 6 | 7 | this.name = this.constructor.name; 8 | this.code = options?.code ?? ''; 9 | this.suggestions = options?.suggestions ?? []; 10 | 11 | Error.captureStackTrace(this, this.constructor); 12 | } 13 | 14 | code?: string; 15 | suggestions?: string[]; 16 | } 17 | -------------------------------------------------------------------------------- /src/common/transformers/file-in-memory.transformer.ts: -------------------------------------------------------------------------------- 1 | import { Transform, TransformCallback } from 'stream'; 2 | 3 | export class FileInMemoryTransformer extends Transform { 4 | constructor() { 5 | super(); 6 | 7 | this.memory = Buffer.alloc(0); 8 | } 9 | 10 | private memory: Buffer; 11 | 12 | _transform(chunk: Buffer, _: string, cb: TransformCallback): void { 13 | this.memory = Buffer.concat([this.memory, chunk]); 14 | cb(); 15 | } 16 | 17 | _flush(cb: TransformCallback): void { 18 | this.push(this.memory); 19 | cb(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/mockfs/unzip.ts: -------------------------------------------------------------------------------- 1 | import { createReadStream } from 'fs'; 2 | import unzipper from 'unzipper'; 3 | 4 | export async function getUnzipedFilesInMap( 5 | path: string, 6 | ): Promise> { 7 | const mapFiles = new Map(); 8 | 9 | await new Promise((resolve, reject) => { 10 | createReadStream(path) 11 | .pipe(unzipper.Parse()) 12 | .on('entry', entry => { 13 | mapFiles.set(entry.path, true); 14 | }) 15 | .on('finish', () => resolve()) 16 | .on('error', reject); 17 | }); 18 | 19 | return mapFiles; 20 | } 21 | -------------------------------------------------------------------------------- /src/common/fs.ts: -------------------------------------------------------------------------------- 1 | import { promisify } from 'util'; 2 | import * as gracefulFs from 'graceful-fs'; 3 | 4 | export const readdirAsync = promisify(gracefulFs.readdir); 5 | export const statAsync = promisify(gracefulFs.stat); 6 | export const safeCreateReadStream = gracefulFs.createReadStream; 7 | export const safeCreateWriteStream = gracefulFs.createWriteStream; 8 | export const safeExistsSync = gracefulFs.existsSync; 9 | export const safeMkdirSync = gracefulFs.mkdirSync; 10 | export const safeReadFileSync = gracefulFs.readFileSync; 11 | export const safeStatSync = gracefulFs.statSync; 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "🐛 Bug Report" 3 | about: Report a reproducible bug or regression. 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Current Behavior 11 | 12 | 13 | 14 | ## Expected Behavior 15 | 16 | 17 | 18 | ## Steps to Reproduce the Problem 19 | 20 | 1. 21 | 1. 22 | 1. 23 | 24 | ## Environment 25 | 26 | - Version: 27 | - Platform: 28 | - Node.js Version: 29 | -------------------------------------------------------------------------------- /src/common/string-stream.ts: -------------------------------------------------------------------------------- 1 | import { Readable } from 'stream'; 2 | 3 | // credits: https://github.com/feross/string-to-stream 4 | export class StringStream extends Readable { 5 | constructor( 6 | protected readonly str: string, 7 | protected readonly encoding?: 'utf8', 8 | ) { 9 | super(); 10 | 11 | this.encoding = encoding || 'utf8'; 12 | } 13 | 14 | protected ended?: boolean; 15 | 16 | _read() { 17 | if (!this.ended) { 18 | process.nextTick(() => { 19 | this.push(Buffer.from(this.str, this.encoding)); 20 | this.push(null); 21 | }); 22 | this.ended = true; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/common/string-to-uint.ts: -------------------------------------------------------------------------------- 1 | import { Readable } from 'stream'; 2 | 3 | export function streamToUInt8Array(stream: Readable): Promise { 4 | const chunks: Buffer[] = []; 5 | return new Promise((resolve, reject) => { 6 | stream.on('data', chunk => chunks.push(Buffer.from(chunk))); 7 | stream.on('error', err => reject(err)); 8 | stream.on('end', () => { 9 | const b = Buffer.concat(chunks); 10 | 11 | resolve( 12 | new Uint8Array( 13 | b.buffer, 14 | b.byteOffset, 15 | b.byteLength / Uint8Array.BYTES_PER_ELEMENT, 16 | ), 17 | ); 18 | }); 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /test/example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-modules-packer-example", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "postinstall": "npx npm-install-peers" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@h4ad/serverless-adapter": "2.10.0", 13 | "is-date-object": "1.0.5" 14 | }, 15 | "devDependencies": { 16 | "is-string": "1.0.7" 17 | }, 18 | "peerDependencies": { 19 | "is-plain-object": "5.0.0", 20 | "kind-of": "6.0.3" 21 | }, 22 | "peerDependenciesMeta": { 23 | "kind-of": { 24 | "optional": true 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/common/custom-command.ts: -------------------------------------------------------------------------------- 1 | import { Command } from '@oclif/core'; 2 | import CustomError from './custom-error'; 3 | import JsonError from './json-error'; 4 | import JsonResult from './json-result'; 5 | 6 | export default abstract class CustomCommand extends Command { 7 | static enableJsonFlag = true; 8 | 9 | protected toSuccessJson(result: object): JsonResult { 10 | return { status: 'success', ...result }; 11 | } 12 | 13 | protected toErrorJson(error: CustomError): JsonError { 14 | const result = { 15 | status: 'error', 16 | message: error.message, 17 | code: error.code, 18 | sugestions: error.suggestions, 19 | }; 20 | 21 | return result; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/common/transformers/memory-stream.transformer.ts: -------------------------------------------------------------------------------- 1 | import { Transform } from 'stream'; 2 | 3 | export class MemoryStream extends Transform { 4 | constructor(private readonly desiredChunkSize: number) { 5 | super(); 6 | 7 | this.memory = Buffer.alloc(0); 8 | } 9 | 10 | private memory: Buffer; 11 | 12 | _transform(chunk: Buffer, _: string, cb: () => void): void { 13 | if ( 14 | Buffer.byteLength(this.memory) + Buffer.byteLength(chunk) >= 15 | this.desiredChunkSize 16 | ) { 17 | this.push(this.memory); 18 | 19 | this.memory = Buffer.alloc(0); 20 | } 21 | 22 | this.memory = Buffer.concat([this.memory, chunk]); 23 | 24 | cb(); 25 | } 26 | 27 | _flush(cb: () => void): void { 28 | this.push(this.memory); 29 | 30 | cb(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /.github/workflows/pr.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request 2 | 3 | on: [ pull_request ] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | strategy: 10 | matrix: 11 | node-version: [ 14.x, 16.x, 18.x ] 12 | 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v3 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | 21 | - name: Update NPM 22 | run: npm i -g npm 23 | 24 | - name: Install Lib Dependencies 25 | run: npm ci 26 | 27 | - name: Install Testing Lib Dependencies 28 | working-directory: test/example 29 | run: npm ci 30 | 31 | - name: Build 32 | run: npm run build --if-present 33 | 34 | - name: Run tests 35 | run: npm test 36 | 37 | - name: Get Coverage Info 38 | run: npm run coverage 39 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | branches: 5 | - master 6 | jobs: 7 | release: 8 | name: Release 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v2 13 | with: 14 | fetch-depth: 0 15 | 16 | - name: Setup Node.js 17 | uses: actions/setup-node@v2 18 | with: 19 | node-version: 14.x 20 | 21 | - name: Update NPM 22 | run: npm i -g npm 23 | 24 | - name: Install dependencies 25 | run: npm ci 26 | 27 | - name: Install Testing Lib Dependencies 28 | working-directory: test/example 29 | run: npm ci 30 | 31 | - name: Test 32 | run: npm test 33 | 34 | - name: Build 35 | run: npm run build 36 | 37 | - name: Release 38 | env: 39 | HUSKY: '0' 40 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 41 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 42 | run: npx semantic-release 43 | 44 | - name: Code Coverage 45 | run: npm run coverage 46 | -------------------------------------------------------------------------------- /benchmark/node-modules-packer.js: -------------------------------------------------------------------------------- 1 | const { prepareEnvironment, getSuiteDir, getOutputSize, getFormattedEndTime } = require('./utils'); 2 | const Run = require('@h4ad/node-modules-packer/lib/commands/run').default; 3 | 4 | async function zipDependencies(withMinification) { 5 | await Run.headless({ 6 | dir: getSuiteDir(), 7 | outputFile: 'lib.zip', 8 | outputPath: __dirname, 9 | minify: withMinification, 10 | }); 11 | } 12 | 13 | async function runSuite(withMinification) { 14 | prepareEnvironment(); 15 | 16 | console.log('Running NodeModulesPacker Suite.'); 17 | const time = new Date(); 18 | console.time('NodeModulesPacker'); 19 | 20 | await zipDependencies(withMinification); 21 | 22 | console.timeEnd('NodeModulesPacker'); 23 | console.log('Finished running NodeModulesPacker Suite.'); 24 | 25 | const endTime = new Date() - time; 26 | const outputSize = getOutputSize('lib.zip'); 27 | 28 | console.log(`OutputSize: ${outputSize}`); 29 | 30 | return { 31 | endTime: getFormattedEndTime(endTime), 32 | outputSize, 33 | }; 34 | } 35 | 36 | module.exports = { 37 | runSuite, 38 | }; 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Vinícius Lourenço 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /benchmark/utils.js: -------------------------------------------------------------------------------- 1 | const { join } = require('path'); 2 | const { execSync } = require('child_process'); 3 | const { createWriteStream, statSync } = require('fs'); 4 | 5 | function getSuiteDir() { 6 | return join(__dirname, 'suite'); 7 | } 8 | 9 | function getNodeModulesDir() { 10 | return join(getSuiteDir(), 'node_modules'); 11 | } 12 | 13 | function getOutputStream(outputFileName) { 14 | return createWriteStream(join(__dirname, outputFileName)); 15 | } 16 | 17 | function getOutputSize(outputFileName) { 18 | return `${(statSync(outputFileName).size / (1024 * 1024)).toFixed(2)} MB`; 19 | } 20 | 21 | function getFormattedEndTime(time) { 22 | return `${(time / 1000).toFixed(2)}s`; 23 | } 24 | 25 | function runCommand(command) { 26 | execSync(command, { cwd: getSuiteDir() }); 27 | } 28 | 29 | function prepareEnvironment() { 30 | console.log('Installing fresh dependencies.'); 31 | runCommand('npm ci --loglevel error'); 32 | console.log('Dependencies installed.'); 33 | } 34 | 35 | module.exports = { 36 | getSuiteDir, 37 | getNodeModulesDir, 38 | getOutputStream, 39 | getOutputSize, 40 | runCommand, 41 | prepareEnvironment, 42 | getFormattedEndTime, 43 | }; 44 | -------------------------------------------------------------------------------- /src/common/transformers/uglify-js.transformer.ts: -------------------------------------------------------------------------------- 1 | import { Transform, TransformCallback } from 'stream'; 2 | import { MinifyOptions, minify } from 'terser'; 3 | 4 | export class UglifyJsTransformer extends Transform { 5 | constructor( 6 | protected readonly filePath: string, 7 | protected readonly uglifyOptions: MinifyOptions = {}, 8 | ) { 9 | super(); 10 | } 11 | 12 | protected chunks: number = 0; 13 | 14 | async _transform( 15 | chunk: Buffer, 16 | encoding: string, 17 | callback: TransformCallback, 18 | ): Promise { 19 | if (this.chunks > 0) { 20 | return callback( 21 | new Error( 22 | 'This transformer should not be called more than once. Check if you use MemoryStream before using UglifyJsTransformer', 23 | ), 24 | ); 25 | } 26 | 27 | console.log(`${this.filePath}:${chunk.byteLength}`); 28 | 29 | const code = chunk.toString('utf-8'); 30 | const result = await minify(code, this.uglifyOptions).catch(() => null); 31 | const data = 32 | !result || !result.code ? chunk : Buffer.from(result.code, 'utf-8'); 33 | 34 | this.chunks++; 35 | 36 | this.push(data); 37 | 38 | callback(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "branches": [ 3 | { 4 | "name": "master" 5 | } 6 | ], 7 | "plugins": [ 8 | [ 9 | "@semantic-release/commit-analyzer", 10 | { 11 | "preset": "angular", 12 | "releaseRules": [ 13 | { 14 | "type": "docs", 15 | "scope": "readme", 16 | "release": "patch" 17 | }, 18 | { 19 | "type": "build", 20 | "release": "patch" 21 | }, 22 | { 23 | "type": "ci", 24 | "scope": "deps", 25 | "release": "patch" 26 | } 27 | ] 28 | } 29 | ], 30 | "@semantic-release/release-notes-generator", 31 | [ 32 | "@semantic-release/changelog", 33 | { 34 | "changelogTitle": "CHANGES:", 35 | "changelogFile": "CHANGELOG.md" 36 | } 37 | ], 38 | "@semantic-release/npm", 39 | "@semantic-release/github", 40 | [ 41 | "@semantic-release/git", 42 | { 43 | "assets": [ 44 | "package.json", 45 | "CHANGELOG.md" 46 | ], 47 | "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" 48 | } 49 | ] 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /test/commands/run/string-stream.test.ts: -------------------------------------------------------------------------------- 1 | import { Readable } from 'stream'; 2 | import { expect } from 'chai'; 3 | import { streamToUInt8Array } from '../../../src/common/string-to-uint'; 4 | 5 | describe('streamToUInt8Array', () => { 6 | it('should convert correctly the readable', async () => { 7 | const ints = 'teste'.split('').map(c => c.charCodeAt(0)); 8 | 9 | // eslint-disable-next-line node/no-unsupported-features/node-builtins 10 | const test = Readable.from([ints]); 11 | 12 | const uint8Array = await streamToUInt8Array(test); 13 | 14 | expect([...uint8Array.values()]).to.members(ints); 15 | }); 16 | 17 | it('should throw error if readable got error', async () => { 18 | const ints = 'teste'.split('').map(c => c.charCodeAt(0)); 19 | 20 | // eslint-disable-next-line node/no-unsupported-features/node-builtins 21 | const test = Readable.from([ints]); 22 | const fakeError = new Error('fake'); 23 | 24 | test.once('data', () => { 25 | test.emit('error', fakeError); 26 | }); 27 | 28 | let error: Error | null = null; 29 | 30 | await streamToUInt8Array(test).catch(err => { 31 | error = err; 32 | }); 33 | 34 | expect(error!.message).to.eq(fakeError.message); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/mockfs/test.ts: -------------------------------------------------------------------------------- 1 | import { Interfaces, toStandardizedId } from '@oclif/core'; 2 | import { castArray } from '@oclif/core/lib/util'; 3 | import test from '@oclif/test'; 4 | import { loadConfig } from '@oclif/test/lib/load-config'; 5 | import MockFsFactory from './mockfs.factory'; 6 | 7 | export function fsmockCommand( 8 | args: string[] | string, 9 | opts: loadConfig.Options = {}, 10 | ): { 11 | run(ctx: { config: Interfaces.Config; expectation: string }): Promise; 12 | finally(): void; 13 | } { 14 | return { 15 | async run(ctx: { config: Interfaces.Config; expectation: string }) { 16 | if (!ctx.config || opts.reset) 17 | ctx.config = await loadConfig(opts).run({} as any); 18 | args = castArray(args); 19 | const [id, ...extra] = args; 20 | const cmdId = toStandardizedId(id, ctx.config); 21 | ctx.expectation = ctx.expectation || `runs ${args?.join(' ')}`; 22 | await ctx.config.runHook('init', { id: cmdId, argv: extra }); 23 | 24 | MockFsFactory.createMockFs(); 25 | await ctx.config.runCommand(cmdId, extra); 26 | }, 27 | finally() { 28 | MockFsFactory.resetMockFs(); 29 | }, 30 | }; 31 | } 32 | 33 | export const fsTest = test.register('fsmockCommand', fsmockCommand); 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🌈 Feature request 3 | about: Suggest an amazing new idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Feature Request 11 | 12 | **Is your feature request related to a problem? Please describe.** 13 | 14 | 15 | **Describe the solution you'd like** 16 | 17 | 18 | **Describe alternatives you've considered** 19 | 20 | 21 | ## Are you willing to resolve this issue by submitting a Pull Request? 22 | 23 | 26 | 27 | - [ ] Yes, I have the time, and I know how to start. 28 | - [ ] Yes, I have the time, but I don't know how to start. I would need guidance. 29 | - [ ] No, I don't have the time, although I believe I could do it if I had the time... 30 | - [ ] No, I don't have the time and I wouldn't even know how to start. 31 | 32 | 35 | -------------------------------------------------------------------------------- /benchmark/suites.js: -------------------------------------------------------------------------------- 1 | const npmPrune = require('./npm-prune'); 2 | const lib = require('./node-modules-packer'); 3 | 4 | const pruneResults = []; 5 | const pruneSkipRestoreResults = []; 6 | const libResults = []; 7 | const libMinifiedResults = []; 8 | 9 | const runs = 5; 10 | 11 | async function runSuites() { 12 | for (let i = 0; i < runs; i++) { 13 | const result = await npmPrune.runSuite(); 14 | 15 | pruneResults.push(result); 16 | } 17 | 18 | for (let i = 0; i < runs; i++) { 19 | const result = await npmPrune.runSuite(true); 20 | 21 | pruneSkipRestoreResults.push(result); 22 | } 23 | 24 | for (let i = 0; i < runs; i++) { 25 | const result = await lib.runSuite(); 26 | 27 | libResults.push(result); 28 | } 29 | 30 | for (let i = 0; i < runs; i++) { 31 | const result = await lib.runSuite(true); 32 | 33 | libMinifiedResults.push(result); 34 | } 35 | 36 | console.log('Npm Prune Results:'); 37 | console.table(pruneResults); 38 | 39 | console.log('Npm Prune (skipping restore) Results:'); 40 | console.table(pruneSkipRestoreResults); 41 | 42 | console.log('Node Modules Packer Results:'); 43 | console.table(libResults); 44 | 45 | console.log('Node Modules Packer Results (minify):'); 46 | console.table(libMinifiedResults); 47 | } 48 | 49 | runSuites(); 50 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | ### Description of change 9 | 10 | 23 | 24 | ### Pull-Request Checklist 25 | 26 | 31 | 32 | - [ ] Code is up-to-date with the `main` branch 33 | - [ ] `npm run lint` passes with this change 34 | - [ ] `npm run test` passes with this change 35 | - [ ] This pull request links relevant issues as `Fixes #0000` 36 | - [ ] There are new or updated unit tests validating the change 37 | - [ ] The new commits follow conventions outlined in the [conventional commit spec](https://www.conventionalcommits.org/en/v1.0.0/) 38 | 39 | 42 | -------------------------------------------------------------------------------- /src/common/extensions.ts: -------------------------------------------------------------------------------- 1 | export const defaultIgnoredFileExtensions = [ 2 | '.spec.js', 3 | '.ts', 4 | '.d.ts', 5 | '.js.map', 6 | '.cjs.map', 7 | '.mjs.map', 8 | '.d.ts.map', 9 | '.css.map', 10 | '.md', 11 | '.txt', 12 | '.png', 13 | '.h', 14 | '.c', 15 | 'Jenkinsfile', 16 | 'Makefile', 17 | 'Gulpfile.js', 18 | 'Gruntfile.js', 19 | 'gulpfile.js', 20 | '.DS_Store', 21 | '.tern-project', 22 | '.gitattributes', 23 | '.editorconfig', 24 | '.eslintrc', 25 | 'eslint', 26 | '.eslintrc.js', 27 | '.eslintrc.json', 28 | '.eslintrc.yml', 29 | '.eslintignore', 30 | '.stylelintrc', 31 | 'stylelint.config.js', 32 | '.stylelintrc.json', 33 | '.stylelintrc.yaml', 34 | '.stylelintrc.yml', 35 | '.stylelintrc.js', 36 | '.htmllintrc', 37 | 'htmllint.js', 38 | '.lint', 39 | '.npmrc', 40 | '.npmignore', 41 | '.jshintrc', 42 | '.flowconfig', 43 | '.documentup.json', 44 | '.yarn-metadata.json', 45 | '.travis.yml', 46 | 'appveyor.yml', 47 | '.gitlab-ci.yml', 48 | 'circle.yml', 49 | '.coveralls.yml', 50 | 'CHANGES', 51 | 'changelog', 52 | 'LICENSE.txt', 53 | 'LICENSE', 54 | 'LICENSE-MIT', 55 | 'LICENSE.BSD', 56 | 'license', 57 | 'AUTHORS', 58 | 'CONTRIBUTORS', 59 | '.yarn-integrity', 60 | '.yarnclean', 61 | '_config.yml', 62 | '.babelrc', 63 | '.yo-rc.json', 64 | 'jest.config.js', 65 | 'karma.conf.js', 66 | 'wallaby.js', 67 | 'wallaby.conf.js', 68 | '.prettierrc', 69 | '.prettierrc.yml', 70 | '.prettierrc.toml', 71 | '.prettierrc.js', 72 | '.prettierrc.json', 73 | 'prettier.config.js', 74 | '.appveyor.yml', 75 | 'tsconfig.json', 76 | 'tslint.json', 77 | ]; 78 | -------------------------------------------------------------------------------- /benchmark/npm-prune.js: -------------------------------------------------------------------------------- 1 | const archiver = require('archiver'); 2 | const { prepareEnvironment, runCommand, getOutputStream, getNodeModulesDir, getOutputSize, getFormattedEndTime } = require('./utils'); 3 | 4 | function cleanDependencies() { 5 | runCommand('npm prune --production --loglevel error'); 6 | } 7 | 8 | function restoreDependencies() { 9 | runCommand('npm ci'); 10 | } 11 | 12 | async function zipDependencies() { 13 | const zip = archiver('zip', { 14 | zlib: { level: 9 }, 15 | }); 16 | const outputStream = getOutputStream('prune.zip'); 17 | 18 | zip.directory( 19 | getNodeModulesDir(), 20 | 'node_modules', 21 | ); 22 | 23 | await new Promise((resolve, reject) => { 24 | zip 25 | .on('error', err => reject(err)) 26 | .pipe(outputStream); 27 | 28 | outputStream.on('close', () => resolve()); 29 | zip.finalize(); 30 | }); 31 | } 32 | 33 | async function runSuite(skipRestore) { 34 | prepareEnvironment(); 35 | 36 | const suitName = skipRestore ? 'NpmPrune (skip restore)' : 'NpmPrune'; 37 | 38 | console.log('Running NpmPrune Suite.'); 39 | const time = new Date(); 40 | console.time(suitName); 41 | 42 | cleanDependencies(); 43 | await zipDependencies(); 44 | 45 | if (!skipRestore) 46 | restoreDependencies(); 47 | 48 | console.timeEnd(suitName); 49 | console.log(`Finished running ${suitName} Suite.`); 50 | 51 | const endTime = new Date() - time; 52 | const outputSize = getOutputSize('prune.zip'); 53 | 54 | console.log(`OutputSize: ${outputSize}`); 55 | 56 | return { 57 | endTime: getFormattedEndTime(endTime), 58 | outputSize, 59 | }; 60 | } 61 | 62 | module.exports = { 63 | runSuite, 64 | }; 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .clinic 9 | *.zip 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 13 | 14 | # Runtime data 15 | pids 16 | *.pid 17 | *.seed 18 | *.pid.lock 19 | 20 | # Directory for instrumented libs generated by jscoverage/JSCover 21 | lib-cov 22 | 23 | # Coverage directory used by tools like istanbul 24 | coverage 25 | *.lcov 26 | 27 | # nyc test coverage 28 | .nyc_output 29 | 30 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 31 | .grunt 32 | 33 | # Bower dependency directory (https://bower.io/) 34 | bower_components 35 | 36 | # node-waf configuration 37 | .lock-wscript 38 | 39 | # Compiled binary addons (https://nodejs.org/api/addons.html) 40 | build/Release 41 | 42 | # Dependency directories 43 | node_modules/ 44 | jspm_packages/ 45 | 46 | # Snowpack dependency directory (https://snowpack.dev/) 47 | web_modules/ 48 | 49 | # TypeScript cache 50 | *.tsbuildinfo 51 | 52 | # Optional npm cache directory 53 | .npm 54 | 55 | # Optional eslint cache 56 | .eslintcache 57 | 58 | # Microbundle cache 59 | .rpt2_cache/ 60 | .rts2_cache_cjs/ 61 | .rts2_cache_es/ 62 | .rts2_cache_umd/ 63 | 64 | # Optional REPL history 65 | .node_repl_history 66 | 67 | # Output of 'npm pack' 68 | *.tgz 69 | 70 | # Yarn Integrity file 71 | .yarn-integrity 72 | 73 | # dotenv environment variables file 74 | .env.test 75 | .env.local 76 | 77 | # parcel-bundler cache (https://parceljs.org/) 78 | .cache 79 | .parcel-cache 80 | 81 | # Next.js build output 82 | .next 83 | out 84 | 85 | # Nuxt.js build / generate output 86 | .nuxt 87 | dist 88 | 89 | # Gatsby files 90 | .cache/ 91 | # Comment in the public line in if your project uses Gatsby and not Next.js 92 | # https://nextjs.org/blog/next-9-1#public-directory-support 93 | # public 94 | 95 | # vuepress build output 96 | .vuepress/dist 97 | 98 | # Serverless directories 99 | .serverless/ 100 | 101 | # FuseBox cache 102 | .fusebox/ 103 | 104 | # DynamoDB Local files 105 | .dynamodb/ 106 | 107 | # TernJS port file 108 | .tern-port 109 | 110 | # Stores VSCode versions used for testing VSCode extensions 111 | .vscode-test 112 | 113 | # yarn v2 114 | .yarn/cache 115 | .yarn/unplugged 116 | .yarn/build-state.yml 117 | .yarn/install-state.gz 118 | .pnp.* 119 | 120 | # Compiled code 121 | lib/ 122 | 123 | # IDEs 124 | .idea 125 | 126 | # tsdoc 127 | temp 128 | /etc 129 | !etc/.gitkeep 130 | 131 | # docs 132 | www/api/* 133 | !www/api/.gitkeep 134 | 135 | .env 136 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | - cron: '36 7 * * 6' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v2 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v1 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v1 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v1 72 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | CHANGES: 2 | 3 | # [1.3.0](https://github.com/H4ad/node-modules-packer/compare/v1.2.2...v1.3.0) (2022-12-04) 4 | 5 | 6 | ### Features 7 | 8 | * **nodejs14:** dropped support for nodejs 12.x ([e9056f7](https://github.com/H4ad/node-modules-packer/commit/e9056f7c0d78e88ef45d9b904d7bf79664be6a17)) 9 | * **npm-v9:** bump version of dependency extractor ([c42e7aa](https://github.com/H4ad/node-modules-packer/commit/c42e7aaeda681e07b44eed2c3f28606ecb5c91ee)) 10 | 11 | ## [1.2.2](https://github.com/H4ad/node-modules-packer/compare/v1.2.1...v1.2.2) (2022-10-23) 12 | 13 | 14 | ### Bug Fixes 15 | 16 | * **graceful-fs:** added to solve issues with too many open files ([58609ca](https://github.com/H4ad/node-modules-packer/commit/58609ca18ba0f962efbaa53d523650e18e8d5001)) 17 | 18 | ## [1.2.1](https://github.com/H4ad/node-modules-packer/compare/v1.2.0...v1.2.1) (2022-09-11) 19 | 20 | 21 | ### Bug Fixes 22 | 23 | * **index:** issues with win32 paths ([3ddcf0e](https://github.com/H4ad/node-modules-packer/commit/3ddcf0e3d0d8504a07cd83a79359eb8a364ba6c1)) 24 | 25 | # [1.2.0](https://github.com/H4ad/node-modules-packer/compare/v1.1.0...v1.2.0) (2022-08-14) 26 | 27 | 28 | ### Features 29 | 30 | * **transformer:** added initial draft for transformer uglify ([c505b7e](https://github.com/H4ad/node-modules-packer/commit/c505b7e8aa591cd13d553fddba38d630a7fd7ac0)) 31 | * **zip:** added implementation with promises ([4634c31](https://github.com/H4ad/node-modules-packer/commit/4634c314f8454164dd1dfc0d9cc21a4e192f3178)) 32 | 33 | # [1.1.0](https://github.com/H4ad/node-modules-packer/compare/v1.0.2...v1.1.0) (2022-08-14) 34 | 35 | 36 | ### Bug Fixes 37 | 38 | * **bin:** fixed problem with executable permission of dev and run ([84dcb6b](https://github.com/H4ad/node-modules-packer/commit/84dcb6b83a5fe3a969a2af3993a68f8e406ea3ce)) 39 | 40 | 41 | ### Features 42 | 43 | * **run:** added path remapping for --include flag ([f199462](https://github.com/H4ad/node-modules-packer/commit/f19946262dd8472b6b00b940e728cd1c6cd3ab99)) 44 | 45 | ## [1.0.2](https://github.com/H4ad/node-modules-packer/compare/v1.0.1...v1.0.2) (2022-07-28) 46 | 47 | ## [1.0.1](https://github.com/H4ad/node-modules-packer/compare/v1.0.0...v1.0.1) (2022-07-28) 48 | 49 | # 1.0.0 (2022-07-28) 50 | 51 | 52 | ### Features 53 | 54 | * **extensions:** added more useless file extensions ([0a3b5c7](https://github.com/H4ad/node-modules-packer/commit/0a3b5c7e368a8fdea562c6faa0784b1827fc569c)) 55 | * **node-modules-packer:** added initial version of the cli ([185bed6](https://github.com/H4ad/node-modules-packer/commit/185bed6c6f89cb6476481aae16def3e6ee93a913)) 56 | 57 | 58 | ### Performance Improvements 59 | 60 | * **archiver:** removed archiver for yazl & fixed memory leak in tests ([3dbc585](https://github.com/H4ad/node-modules-packer/commit/3dbc5852c7d619e76fa74c366f542b104e98d7a1)) 61 | * **zip:** refactored to use another lib instead archiver ([d4d06f5](https://github.com/H4ad/node-modules-packer/commit/d4d06f57bb785197e86cb00f69a8e8317a3a801e)) 62 | -------------------------------------------------------------------------------- /benchmark/README.md: -------------------------------------------------------------------------------- 1 | # Benchmark 2 | 3 | A wise man once said: 4 | 5 | > Not trying to measure our members, so to speak, but you must log more data next time. @jets-fool, 2022. 6 | 7 | And actions prove more than words, here are my numbers: 8 | 9 | ## About the suits 10 | 11 | [NpmPrune](./npm-prune.js): 12 | 13 | - Clean the dependencies with `npm prune --production` 14 | - Zip the node_modules with `archiver` 15 | - Restore the dependencies back (this can be skipped if you run on CI/CD) 16 | 17 | [NpmPrune (skipping restore)](./npm-prune.js): 18 | 19 | - Clean the dependencies with `npm prune --production` 20 | - Zip the node_modules with `archiver` 21 | 22 | [Node Modules Packer](./node-modules-packer.js): 23 | 24 | - Just zip the node_modules, that's it. 25 | 26 | ## How to Run 27 | 28 | First, install dependencies and build the library with: 29 | 30 | ```bash 31 | # inside root folder 32 | npm ci 33 | npm run build 34 | # inside benchmark folder 35 | npm ci 36 | ``` 37 | 38 | Then run this script: 39 | 40 | ```bash 41 | npm run benchmark 42 | ``` 43 | 44 | ## Results 45 | 46 | Setup: Ryzen 3 2200g, 32 GB 3200MHz, SSD m2 3500MB/s read, 3000MB/s write. 47 | 48 | Npm Prune Results: 49 | 50 | | (index) | endTime | outputSize | 51 | |---------|---------|------------| 52 | | 0 | 38.24s | 29.31 MB | 53 | | 1 | 39.64s | 29.31 MB | 54 | | 2 | 40.44s | 29.31 MB | 55 | | 3 | 41.91s | 29.31 MB | 56 | | 4 | 35.80s | 29.31 MB | 57 | 58 | Npm Prune (skipping restore) Results: 59 | 60 | | (index) | endTime | outputSize | 61 | |---------|---------|------------| 62 | | 0 | 23.56s | 29.31 MB | 63 | | 1 | 28.76s | 29.31 MB | 64 | | 2 | 23.43s | 29.31 MB | 65 | | 3 | 23.22s | 29.31 MB | 66 | | 4 | 23.09s | 29.31 MB | 67 | 68 | Node Modules Packer Results: 69 | 70 | | (index) | endTime | outputSize | 71 | |---------|---------|------------| 72 | | 0 | 7.48s | 17.44 MB | 73 | | 1 | 7.07s | 17.44 MB | 74 | | 2 | 7.04s | 17.44 MB | 75 | | 3 | 7.28s | 17.44 MB | 76 | | 4 | 7.39s | 17.44 MB | 77 | 78 | Node Modules Packer (minified) Results: 79 | 80 | | (index) | endTime | outputSize | 81 | |---------|---------|------------| 82 | | 0 | 10.13s | 13.19 MB | 83 | | 1 | 10.03s | 13.19 MB | 84 | | 2 | 9.99s | 13.19 MB | 85 | | 3 | 10.45s | 13.19 MB | 86 | | 4 | 10.38s | 13.19 MB | 87 | 88 | Summary: 89 | 90 | | suite | endTime (mean) | outputSize (mean) | 91 | |--------------------------------|----------------------|-------------------------| 92 | | Npm Prune | 39.21s | 29.31 MB | 93 | | Npm Prune (skipping restore) | 24.41s (61% faster) | 29.31 MB (0% lighter) | 94 | | Node Modules Packer | 7.25s (441% faster) | 17.44 MB (~40% lighter) | 95 | | Node Modules Packer (minified) | 10.20s (284% faster) | 13.19 MB (~55% lighter) | 96 | 97 | So running this library without doing any optimization can give you a speed improvement of 441% (284% minified) and 98 | reducing your zip file by ~40% (~55% minified), you can further reduce the size of the zip [with a few more tweaks](./README.md#examples). 99 | 100 | But, I don't know about you, but for me the big benefit is not having to run `npm prune --production`, 101 | I use Webstorm and this command makes my Webstorm re-index all node_modules making me lose more seconds of my life . 102 | -------------------------------------------------------------------------------- /src/common/headless.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The interface to represents options to customize running this library in headless mode. 3 | */ 4 | export interface HeadlessOptions { 5 | /** 6 | * The directory to pack the node modules 7 | * 8 | * @example ./ 9 | */ 10 | dir: string; 11 | 12 | /** 13 | * The files you want to include with the node_modules 14 | * 15 | * @example ['dist', 'index.js] 16 | * @default [] 17 | */ 18 | include?: string[]; 19 | 20 | /** 21 | * The file extensions you want to exclude from node_modules, 22 | * this option dosn't not affect files outside node_modules. 23 | * 24 | * @example ['.json', '.d.ts'] 25 | * @default {@link import('./extensions').defaultIgnoredFileExtensions} 26 | */ 27 | ignoreFileExt?: string[]; 28 | 29 | /** 30 | * By default, we append {@link import('./extensions').defaultIgnoredFileExtensions} 31 | * with the extensions you provide, but if you want to include some extension that is listed 32 | * inside {@link import('./extensions').defaultIgnoredFileExtensions}, you should enable 33 | * this flag. 34 | * 35 | * It's highly recommended you keep this flag disabled. 36 | */ 37 | disableDefaultIgnoreFileExt?: boolean; 38 | 39 | /** 40 | * The node_modules paths you want to include inside your zip file. 41 | * For example, you have some dependency that was marked as dev dependency wrong, 42 | * so you want to include that dependency, just set: `['my-dependency']`. 43 | * This will be turned into `node_modules/my-dependency` and any file starting in this 44 | * path will be included. 45 | * 46 | * @example ['my-dependency'] 47 | * @default [] 48 | */ 49 | includeNodePath?: string[]; 50 | 51 | /** 52 | * This flag is oposite of {@link includeNodePath}, with this flag you can remove unwanted 53 | * node_module paths, like `typeorm/browser` which is included when you zip our dependencies 54 | * even with `npm prune --production`. 55 | * 56 | * @example ['typeorm/browser'] 57 | * @default [] 58 | */ 59 | ignoreNodePath?: string[]; 60 | 61 | /** 62 | * Tells if we should include production dependencies. 63 | * 64 | * @default true 65 | */ 66 | prod?: boolean; 67 | 68 | /** 69 | * Tells if we should include development dependencies. 70 | * 71 | * @default false 72 | */ 73 | dev?: boolean; 74 | 75 | /** 76 | * Tells if we should include peer dependencies. 77 | * 78 | * @default false 79 | */ 80 | peer?: boolean; 81 | 82 | /** 83 | * Tells if we should include optional dependencies. 84 | * 85 | * @default false 86 | */ 87 | optional?: boolean; 88 | 89 | /** 90 | * The output path for the zip file. 91 | * 92 | * @example ./some/path 93 | * @default ./ 94 | */ 95 | outputPath?: string; 96 | 97 | /** 98 | * The output filename for the zip. 99 | * 100 | * If you choose some name ending with .tar.gz, the library will compress with tar instead gzip. 101 | * 102 | * @example result.zip or result.tar.gz 103 | * @default deploy.zip 104 | */ 105 | outputFile?: string; 106 | 107 | /** 108 | * Pass all .js files to esbuild transform to reduce the file size. 109 | * 110 | * @default false 111 | */ 112 | minify?: boolean; 113 | 114 | /** 115 | * Keep the names of all properties and class names during minification. 116 | * 117 | * @reference {@link https://esbuild.github.io/api/#keep-names} 118 | * @default false 119 | */ 120 | minifyKeepNames?: boolean; 121 | } 122 | -------------------------------------------------------------------------------- /benchmark/suite/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nestjs-boilerplate", 3 | "version": "0.0.1", 4 | "description": "", 5 | "author": "", 6 | "private": true, 7 | "license": "UNLICENSED", 8 | "scripts": { 9 | "pretypeorm": "ts-node -r tsconfig-paths/register ./src/database/scripts/copy-typeorm-config.ts", 10 | "typeorm": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js", 11 | "seeds": "ts-node -r tsconfig-paths/register ./node_modules/typeorm-seeding/dist/cli.js", 12 | "migration:generate": "npm run typeorm -- migration:generate -n", 13 | "migration:create": "npm run typeorm -- migration:create -n", 14 | "migration:run": "npm run typeorm -- migration:run", 15 | "migration:revert": "npm run typeorm -- migration:revert", 16 | "schema:drop": "npm run typeorm -- schema:drop", 17 | "seed:config": "npm run seeds -- config", 18 | "seed:run": "npm run seeds -- seed", 19 | "prebuild": "rimraf dist", 20 | "build": "nest build", 21 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", 22 | "start": "nest start", 23 | "start:dev": "nest start --watch", 24 | "start:debug": "nest start --debug --watch", 25 | "start:prod": "node dist/main", 26 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", 27 | "test": "jest", 28 | "test:watch": "jest --watch", 29 | "test:cov": "jest --coverage", 30 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 31 | "test:e2e": "env-cmd jest --config ./test/jest-e2e.json" 32 | }, 33 | "dependencies": { 34 | "@nestjs-modules/mailer": "1.6.1", 35 | "@nestjs/common": "8.4.7", 36 | "@nestjs/config": "2.1.0", 37 | "@nestjs/core": "8.4.7", 38 | "@nestjs/jwt": "8.0.1", 39 | "@nestjs/passport": "8.2.2", 40 | "@nestjs/platform-express": "8.4.7", 41 | "@nestjs/swagger": "5.2.1", 42 | "@nestjs/typeorm": "8.0.3", 43 | "apple-signin-auth": "1.5.1", 44 | "bcryptjs": "2.4.3", 45 | "class-transformer": "0.5.1", 46 | "class-validator": "0.13.2", 47 | "fb": "2.0.0", 48 | "google-auth-library": "8.0.1", 49 | "handlebars": "4.7.7", 50 | "multer": "1.4.4", 51 | "multer-s3": "2.10.0", 52 | "nestjs-i18n": "8.3.2", 53 | "nodemailer": "6.7.6", 54 | "passport": "0.6.0", 55 | "passport-anonymous": "1.0.1", 56 | "passport-jwt": "4.0.0", 57 | "pg": "8.7.3", 58 | "reflect-metadata": "0.1.13", 59 | "rimraf": "3.0.2", 60 | "rxjs": "7.5.5", 61 | "source-map-support": "0.5.21", 62 | "swagger-ui-express": "4.3.0", 63 | "twitter": "1.7.1", 64 | "typeorm": "0.2.43", 65 | "typeorm-seeding": "1.6.1" 66 | }, 67 | "devDependencies": { 68 | "@nestjs/cli": "8.2.8", 69 | "@nestjs/schematics": "8.0.11", 70 | "@nestjs/testing": "8.4.7", 71 | "@types/bcryptjs": "2.4.2", 72 | "@types/express": "4.17.13", 73 | "@types/facebook-js-sdk": "3.3.5", 74 | "@types/jest": "27.5.1", 75 | "@types/multer": "1.4.7", 76 | "@types/node": "16.11.41", 77 | "@types/passport-anonymous": "1.0.3", 78 | "@types/passport-jwt": "3.0.6", 79 | "@types/supertest": "2.0.12", 80 | "@types/twitter": "1.7.1", 81 | "@typescript-eslint/eslint-plugin": "5.25.0", 82 | "@typescript-eslint/parser": "5.25.0", 83 | "aws-sdk": "2.1137.0", 84 | "env-cmd": "10.1.0", 85 | "eslint": "8.19.0", 86 | "eslint-config-prettier": "8.5.0", 87 | "eslint-plugin-import": "2.26.0", 88 | "eslint-plugin-prettier": "4.0.0", 89 | "husky": "7.0.4", 90 | "is-ci": "3.0.1", 91 | "jest": "28.1.0", 92 | "prettier": "2.6.2", 93 | "supertest": "6.2.4", 94 | "ts-jest": "28.0.1", 95 | "ts-loader": "9.3.1", 96 | "ts-node": "10.7.0", 97 | "tsconfig-paths": "4.0.0", 98 | "tslib": "2.4.0", 99 | "typescript": "4.6.4" 100 | }, 101 | "jest": { 102 | "moduleFileExtensions": [ 103 | "js", 104 | "json", 105 | "ts" 106 | ], 107 | "rootDir": "src", 108 | "testRegex": ".*\\.spec\\.ts$", 109 | "transform": { 110 | "^.+\\.(t|j)s$": "ts-jest" 111 | }, 112 | "collectCoverageFrom": [ 113 | "**/*.(t|j)s" 114 | ], 115 | "coverageDirectory": "../coverage", 116 | "testEnvironment": "node" 117 | }, 118 | "engines": { 119 | "node": ">=14.0.0" 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/common/zip.ts: -------------------------------------------------------------------------------- 1 | import { Stats } from 'fs'; 2 | import { join, normalize, relative } from 'path'; 3 | import { ZipFile } from 'yazl'; 4 | import { 5 | readdirAsync, 6 | safeCreateReadStream, 7 | safeCreateWriteStream, 8 | statAsync, 9 | } from './fs'; 10 | import { StringStream } from './string-stream'; 11 | import { streamToUInt8Array } from './string-to-uint'; 12 | 13 | export type TransformAsyncCode = (code: Uint8Array) => Promise; 14 | 15 | export interface ZipArtifact { 16 | path: string; 17 | /** 18 | * @deprecated It will be removed in the next versions because it is not used 19 | */ 20 | name: string; 21 | metadataPath?: string; 22 | type: 'file' | 'directory'; 23 | shouldIgnore?: (fileName: string) => boolean; 24 | transformer?: ( 25 | filePath: string, 26 | metadataPath: string, 27 | ) => TransformAsyncCode | undefined; 28 | } 29 | 30 | export class FasterZip { 31 | public async run( 32 | rootPath: string, 33 | outputPath: string, 34 | zipArtifacts: ZipArtifact[], 35 | ): Promise { 36 | await new Promise((resolve, reject) => { 37 | (async () => { 38 | const zipfile = new ZipFile(); 39 | const stream = safeCreateWriteStream(outputPath).once('error', reject); 40 | 41 | zipfile.outputStream.pipe(stream); 42 | 43 | for (const artifact of zipArtifacts) { 44 | await this.handleArtifact(artifact, zipfile, rootPath, reject).catch( 45 | reject, 46 | ); 47 | } 48 | 49 | zipfile.end(); 50 | 51 | stream.once('error', reject).once('close', () => resolve()); 52 | })(); 53 | }); 54 | } 55 | 56 | protected async readdirAndAddToZip( 57 | zipFile: ZipFile, 58 | rootPath: string, 59 | source: ZipArtifact, 60 | path: string, 61 | onErrorOnStream: (reason?: any) => void, 62 | ) { 63 | const filePaths = await readdirAsync(path).then(files => 64 | files.map(file => join(path, file)), 65 | ); 66 | 67 | const filePathsAndStats: [string, Stats][] = await Promise.all( 68 | filePaths.map(async filePath => [filePath, await statAsync(filePath)]), 69 | ); 70 | 71 | await Promise.all( 72 | filePathsAndStats.map(async ([filePath, stat]) => { 73 | if (stat.isFile()) { 74 | if (source.shouldIgnore && source.shouldIgnore(filePath)) return; 75 | 76 | await this.addFileToZip( 77 | zipFile, 78 | source, 79 | rootPath, 80 | filePath, 81 | onErrorOnStream, 82 | ); 83 | } else { 84 | await this.readdirAndAddToZip( 85 | zipFile, 86 | rootPath, 87 | source, 88 | filePath, 89 | onErrorOnStream, 90 | ); 91 | } 92 | }), 93 | ); 94 | } 95 | 96 | protected async addFileToZip( 97 | zipFile: ZipFile, 98 | source: ZipArtifact, 99 | rootPath: string, 100 | filePath: string, 101 | onErrorOnStream: (reason?: any) => void, 102 | ): Promise { 103 | const metadataPath = normalize( 104 | source.metadataPath 105 | ? filePath.replace(source.path, source.metadataPath) 106 | : relative(rootPath, filePath), 107 | ); 108 | 109 | const transformer = 110 | source.transformer && source.transformer(filePath, metadataPath); 111 | 112 | const readStream = safeCreateReadStream(filePath).once('error', err => 113 | onErrorOnStream(err), 114 | ); 115 | 116 | if (transformer) { 117 | try { 118 | const code = await streamToUInt8Array(readStream); 119 | const finalContent = await transformer(code); 120 | 121 | const fileContentReadable = new StringStream(finalContent); 122 | 123 | zipFile.addReadStream(fileContentReadable, metadataPath); 124 | } catch (e) { 125 | zipFile.addReadStream(readStream, metadataPath); 126 | } 127 | } else zipFile.addReadStream(readStream, metadataPath); 128 | } 129 | 130 | protected async handleArtifact( 131 | artifact: ZipArtifact, 132 | zipfile: ZipFile, 133 | rootPath: string, 134 | onErrorOnStream: (reason?: any) => void, 135 | ): Promise { 136 | if (artifact.type === 'directory') { 137 | await this.readdirAndAddToZip( 138 | zipfile, 139 | rootPath, 140 | artifact, 141 | artifact.path, 142 | onErrorOnStream, 143 | ); 144 | } else { 145 | await this.addFileToZip( 146 | zipfile, 147 | artifact, 148 | rootPath, 149 | artifact.path, 150 | onErrorOnStream, 151 | ); 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@h4ad/node-modules-packer", 3 | "version": "1.3.0", 4 | "description": "", 5 | "main": "lib/index.js", 6 | "types": "lib/index.ts", 7 | "scripts": { 8 | "prepare": "husky install", 9 | "build": "rimraf lib && tsc -p tsconfig.build.json", 10 | "postpack": "rimraf oclif.manifest.json", 11 | "prepack": "npm run build && oclif manifest && oclif readme", 12 | "clean": "rm -rf ./lib/", 13 | "cm": "cz", 14 | "coverage": "codecov --disable=gcov", 15 | "lint": "eslint ./src/ ./test/ --fix", 16 | "semantic-release": "semantic-release", 17 | "pretest": "npm run build", 18 | "test": "nyc --extension .ts --reporter=html --reporter=json mocha --forbid-only \"test/**/*.test.ts\"", 19 | "typecheck": "tsc --noEmit", 20 | "version": "oclif readme && git add README.md" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/H4ad/node-modules-packer.git" 25 | }, 26 | "license": "MIT", 27 | "author": { 28 | "name": "Vinícius Lourenço", 29 | "email": "H4ad@users.noreply.github.com", 30 | "url": "https://github.com/H4ad" 31 | }, 32 | "engines": { 33 | "node": ">=14.0" 34 | }, 35 | "keywords": [ 36 | "dependencies", 37 | "node modules", 38 | "packer", 39 | "pack", 40 | "compact", 41 | "compact node modules", 42 | "pack node modules", 43 | "gzip node modules", 44 | "zip node modules", 45 | "serverless", 46 | "serverless bundler" 47 | ], 48 | "bugs": { 49 | "url": "https://github.com/H4ad/node-modules-packer/issues" 50 | }, 51 | "homepage": "https://github.com/H4ad/node-modules-packer#readme", 52 | "files": [ 53 | "/bin", 54 | "/lib", 55 | "/npm-shrinkwrap.json", 56 | "/oclif.manifest.json" 57 | ], 58 | "bin": { 59 | "pack": "./bin/run" 60 | }, 61 | "oclif": { 62 | "bin": "node-modules-packer", 63 | "dirname": "node-modules-packer", 64 | "commands": "./lib/commands", 65 | "default": "run", 66 | "topicSeparator": ":", 67 | "topics": { 68 | "run": { 69 | "description": "Pack files and node dependencies to zip file." 70 | } 71 | }, 72 | "plugins": [ 73 | "@oclif/plugin-help", 74 | "@oclif/plugin-plugins", 75 | "@oclif/plugin-version", 76 | "@oclif/plugin-commands", 77 | "@oclif/plugin-autocomplete" 78 | ] 79 | }, 80 | "dependencies": { 81 | "@h4ad/dependency-extractor": "1.1.0", 82 | "@oclif/core": "^1", 83 | "@oclif/plugin-autocomplete": "^1.3.0", 84 | "@oclif/plugin-commands": "^2.2.0", 85 | "@oclif/plugin-help": "^5", 86 | "@oclif/plugin-plugins": "^2.1.0", 87 | "@oclif/plugin-version": "^1.1.1", 88 | "esbuild": "~0.15.7", 89 | "graceful-fs": "^4.2.10", 90 | "rimraf": "^3.0.2", 91 | "semver": "^7.3.7", 92 | "yazl": "^2.5.1" 93 | }, 94 | "devDependencies": { 95 | "@oclif/test": "^2", 96 | "@semantic-release/changelog": "^6.0.1", 97 | "@semantic-release/git": "^10.0.1", 98 | "@semantic-release/github": "^8.0.4", 99 | "@types/bluebird": "^3.5.36", 100 | "@types/chai": "^4", 101 | "@types/graceful-fs": "^4.1.5", 102 | "@types/mocha": "^9.0.0", 103 | "@types/mock-fs": "^4.13.1", 104 | "@types/node": "14.18.34", 105 | "@types/plist": "^3.0.2", 106 | "@types/rimraf": "^3.0.2", 107 | "@types/terser": "^3.12.0", 108 | "@types/uglify-js": "^3.16.0", 109 | "@types/unzipper": "^0.10.5", 110 | "@types/yazl": "^2.4.2", 111 | "@typescript-eslint/eslint-plugin": "^5.12.1", 112 | "@typescript-eslint/parser": "^5.12.1", 113 | "c8": "^7.12.0", 114 | "chai": "^4", 115 | "codecov": "^3.8.3", 116 | "commitizen": "^4.2.4", 117 | "cz-conventional-changelog": "^3.3.0", 118 | "ejs": "^3.1.6", 119 | "eslint": "^8.9.0", 120 | "eslint-config-oclif": "^4", 121 | "eslint-config-oclif-typescript": "^1.0.2", 122 | "eslint-config-prettier": "^8.4.0", 123 | "eslint-plugin-import": "^2.25.4", 124 | "eslint-plugin-node": "^11.1.0", 125 | "eslint-plugin-prettier": "^4.0.0", 126 | "husky": "^6.0.0", 127 | "lint-staged": "^10.5.4", 128 | "mocha": "^9", 129 | "mock-fs": "^5.1.2", 130 | "nyc": "^15.1.0", 131 | "oclif": "^3", 132 | "prettier": "^2.5.1", 133 | "semantic-release": "^19.0.3", 134 | "tape": "^5.5.3", 135 | "ts-node": "^10.4.0", 136 | "tslib": "^2.4.0", 137 | "typescript": "^4.5.5", 138 | "unzipper": "^0.10.11" 139 | }, 140 | "config": { 141 | "commitizen": { 142 | "path": "./node_modules/cz-conventional-changelog" 143 | } 144 | }, 145 | "lint-staged": { 146 | "*.ts": "eslint --cache --cache-location .eslintcache --fix" 147 | }, 148 | "publishConfig": { 149 | "access": "public" 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": [ 5 | "@typescript-eslint" 6 | ], 7 | "parserOptions": { 8 | "project": [ 9 | "./tsconfig.json" 10 | ] 11 | }, 12 | "extends": [ 13 | "eslint:recommended", 14 | "plugin:node/recommended", 15 | "plugin:import/recommended", 16 | "plugin:import/typescript", 17 | "plugin:@typescript-eslint/eslint-recommended", 18 | "plugin:@typescript-eslint/recommended", 19 | "plugin:@typescript-eslint/recommended-requiring-type-checking", 20 | "plugin:prettier/recommended" 21 | ], 22 | "rules": { 23 | "prettier/prettier": "warn", 24 | "comma-dangle": [ 25 | "error", 26 | "always-multiline" 27 | ], 28 | "node/no-missing-import": "off", 29 | "node/no-empty-function": "off", 30 | "node/no-unsupported-features/es-syntax": "off", 31 | "node/no-missing-require": "off", 32 | "node/shebang": "off", 33 | "import/order": [ 34 | "error", 35 | { 36 | "newlines-between": "never" 37 | } 38 | ], 39 | "sort-imports": [ 40 | "error", 41 | { 42 | "ignoreDeclarationSort": true 43 | } 44 | ], 45 | "@typescript-eslint/no-use-before-define": "off", 46 | "quotes": [ 47 | "warn", 48 | "single", 49 | { 50 | "avoidEscape": true 51 | } 52 | ], 53 | "curly": [ 54 | "error", 55 | "multi-or-nest" 56 | ], 57 | "max-len": [ 58 | "warn", 59 | { 60 | "code": 140, 61 | "tabWidth": 2, 62 | "ignoreStrings": true, 63 | "ignoreTemplateLiterals": true, 64 | "ignoreUrls": true, 65 | "ignoreComments": true, 66 | "ignorePattern": "^import\\s.+\\sfrom\\s.+;$" 67 | } 68 | ], 69 | "no-case-declarations": "warn", 70 | "no-control-regex": "off", 71 | "node/no-unpublished-import": "off", 72 | "@typescript-eslint/no-namespace": "off", 73 | "@typescript-eslint/no-unsafe-argument": "off", 74 | "@typescript-eslint/no-unsafe-assignment": "off", 75 | "@typescript-eslint/no-unsafe-member-access": "off", 76 | "@typescript-eslint/no-unsafe-call": "off", 77 | "@typescript-eslint/no-unsafe-return": "off", 78 | "@typescript-eslint/no-var-requires": "off", 79 | "@typescript-eslint/ban-ts-comment": "off", 80 | "@typescript-eslint/no-explicit-any": "off", 81 | "@typescript-eslint/no-non-null-assertion": "off", 82 | "@typescript-eslint/restrict-template-expressions": "off", 83 | "@typescript-eslint/no-floating-promises": "off", 84 | "@typescript-eslint/explicit-module-boundary-types": "off", 85 | "@typescript-eslint/no-inferrable-types": "off", 86 | "@typescript-eslint/member-ordering": [ 87 | "error", 88 | { 89 | "default": [ 90 | // Constructors 91 | "public-constructor", 92 | "protected-constructor", 93 | "private-constructor", 94 | // Index signature 95 | "signature", 96 | // Fields 97 | "protected-abstract-field", 98 | "public-abstract-field", 99 | "private-abstract-field", 100 | "protected-static-field", 101 | "public-static-field", 102 | "private-static-field", 103 | "protected-decorated-field", 104 | "public-decorated-field", 105 | "private-decorated-field", 106 | "protected-instance-field", 107 | "public-instance-field", 108 | "private-instance-field", 109 | // Getters 110 | "protected-decorated-get", 111 | "public-decorated-get", 112 | "private-decorated-get", 113 | "protected-static-get", 114 | "public-static-get", 115 | "private-static-get", 116 | "protected-instance-get", 117 | "public-instance-get", 118 | "private-instance-get", 119 | "protected-abstract-get", 120 | "public-abstract-get", 121 | "private-abstract-get", 122 | "decorated-get", 123 | "abstract-get", 124 | "protected-get", 125 | "public-get", 126 | "private-get", 127 | "static-get", 128 | "instance-get", 129 | "get", 130 | // Setters 131 | "protected-abstract-set", 132 | "public-abstract-set", 133 | "private-abstract-set", 134 | "abstract-set", 135 | "protected-decorated-set", 136 | "public-decorated-set", 137 | "private-decorated-set", 138 | "protected-static-set", 139 | "public-static-set", 140 | "private-static-set", 141 | "protected-instance-set", 142 | "public-instance-set", 143 | "private-instance-set", 144 | "protected-set", 145 | "public-set", 146 | "private-set", 147 | "decorated-set", 148 | "static-set", 149 | "instance-set", 150 | "set", 151 | // Methods 152 | "public-static-method", 153 | "protected-static-method", 154 | "private-static-method", 155 | "public-decorated-method", 156 | "protected-decorated-method", 157 | "private-decorated-method", 158 | "public-abstract-method", 159 | "protected-abstract-method", 160 | "private-abstract-method", 161 | "public-instance-method", 162 | "protected-instance-method", 163 | "private-instance-method" 164 | ] 165 | } 166 | ] 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tsconfig", 3 | "compilerOptions": { 4 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 5 | 6 | /* Basic Options */ 7 | // "incremental": true, /* Enable incremental compilation */ 8 | "target": "ES2019", 9 | /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ 10 | "module": "commonjs", 11 | /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ 12 | // "lib": [], /* Specify library files to be included in the compilation. */ 13 | // "allowJs": true, /* Allow javascript files to be compiled. */ 14 | // "checkJs": true, /* Report errors in .js files. */ 15 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ 16 | "declaration": true, 17 | /* Generates corresponding '.d.ts' file. */ 18 | "declarationMap": true, 19 | /* Generates a sourcemap for each corresponding '.d.ts' file. */ 20 | "sourceMap": true, 21 | /* Generates corresponding '.map' file. */ 22 | // "outFile": "./", /* Concatenate and emit output to single file. */ 23 | "outDir": "./lib/", 24 | /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 25 | // "composite": true, 26 | /* Enable project compilation */ 27 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 28 | // "removeComments": true, /* Do not emit comments to output. */ 29 | // "noEmit": true, /* Do not emit outputs. */ 30 | "importHelpers": true, 31 | /* Import emit helpers from 'tslib'. */ 32 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 33 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 34 | "resolveJsonModule": true, 35 | /* Enable importing .json files */ 36 | 37 | /* Strict Type-Checking Options */ 38 | "strict": false, 39 | /* Enable all strict type-checking options. */ 40 | "noImplicitAny": false, 41 | /* Raise error on expressions and declarations with an implied 'any' type. */ 42 | "strictNullChecks": true, 43 | /* Enable strict null checks. */ 44 | "strictFunctionTypes": false, 45 | /* Enable strict checking of function types. */ 46 | "strictBindCallApply": true, 47 | /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 48 | "strictPropertyInitialization": true, 49 | /* Enable strict checking of property initialization in classes. */ 50 | "noImplicitThis": false, 51 | /* Raise error on 'this' expressions with an implied 'any' type. */ 52 | "alwaysStrict": true, 53 | /* Parse in strict mode and emit "use strict" for each source file. */ 54 | 55 | /* Additional Checks */ 56 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 57 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 58 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 59 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 60 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 61 | // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ 62 | 63 | /* Module Resolution Options */ 64 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 65 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 66 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 67 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 68 | // "typeRoots": [], /* List of folders to include type definitions from. */ 69 | // "types": [], /* Type declaration files to be included in compilation. */ 70 | "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 71 | "esModuleInterop": true, 72 | /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 73 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 74 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 75 | 76 | /* Source Map Options */ 77 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 78 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 79 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 80 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 81 | 82 | /* Experimental Options */ 83 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 84 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 85 | 86 | /* Advanced Options */ 87 | "skipLibCheck": true, 88 | /* Skip type checking of declaration files. */ 89 | "forceConsistentCasingInFileNames": true 90 | /* Disallow inconsistently-cased references to the same file. */ 91 | }, 92 | "include": [ 93 | "src/**/*.ts", 94 | "test/**/*.ts" 95 | ], 96 | "exclude": [] 97 | } 98 | -------------------------------------------------------------------------------- /test/commands/run/zip.test.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import { join } from 'path'; 4 | import { afterEach } from 'mocha'; 5 | import { expect } from 'chai'; 6 | import { ZipFile } from 'yazl'; 7 | import { FasterZip } from '../../../src/common/zip'; 8 | import MockFsFactory from '../../mockfs/mockfs.factory'; 9 | import { getUnzipedFilesInMap } from '../../mockfs/unzip'; 10 | 11 | describe(ZipFile.name, () => { 12 | beforeEach(() => { 13 | MockFsFactory.createMockFs(); 14 | }); 15 | 16 | afterEach(() => { 17 | MockFsFactory.resetMockFs(); 18 | }); 19 | 20 | describe('should zip correctly', () => { 21 | it('when passing folder', async () => { 22 | const zip = new FasterZip(); 23 | 24 | await zip.run(MockFsFactory.DIR_PROJECT, './zip-file.zip', [ 25 | { 26 | path: join(MockFsFactory.DIR_PROJECT, 'node_modules'), 27 | name: 'node_modules', 28 | type: 'directory', 29 | }, 30 | { 31 | path: join(MockFsFactory.DIR_PROJECT, 'src'), 32 | name: 'src', 33 | type: 'directory', 34 | }, 35 | ]); 36 | 37 | expect(fs.existsSync('zip-file.zip')).to.be.true; 38 | }); 39 | 40 | it('when passing file', async () => { 41 | const zip = new FasterZip(); 42 | 43 | await zip.run(MockFsFactory.DIR_PROJECT, './zip-file.zip', [ 44 | { 45 | path: join(MockFsFactory.DIR_PROJECT, 'package.json'), 46 | name: 'test', 47 | type: 'file', 48 | }, 49 | ]); 50 | 51 | expect(fs.existsSync('zip-file.zip')).to.be.true; 52 | 53 | const outputFilePath = join('zip-file.zip'); 54 | const mapFiles = await getUnzipedFilesInMap(outputFilePath); 55 | 56 | expect(mapFiles.has('package.json')).to.be.true; 57 | }); 58 | 59 | it('when pass with remapping path', async () => { 60 | const zip = new FasterZip(); 61 | 62 | await zip.run(MockFsFactory.DIR_PROJECT, './zip-file.zip', [ 63 | { 64 | path: path.join(MockFsFactory.DIR_PROJECT, 'package.json'), 65 | metadataPath: 'package-lock.json', 66 | name: 'test', 67 | type: 'file', 68 | }, 69 | { 70 | path: path.join(MockFsFactory.DIR_PROJECT, 'src'), 71 | metadataPath: 'dist', 72 | name: 'test', 73 | type: 'directory', 74 | }, 75 | ]); 76 | 77 | expect(fs.existsSync('zip-file.zip')).to.be.true; 78 | 79 | const outputFilePath = join('zip-file.zip'); 80 | const mapFiles = await getUnzipedFilesInMap(outputFilePath); 81 | 82 | expect(mapFiles.has('package-lock.json')).to.be.true; 83 | expect(mapFiles.has('dist/index.js')).to.be.true; 84 | }); 85 | 86 | it('when ignore file', async () => { 87 | const zip = new FasterZip(); 88 | 89 | await zip.run(MockFsFactory.DIR_PROJECT, './zip-file.zip', [ 90 | { 91 | path: path.join(MockFsFactory.DIR_PROJECT, 'src'), 92 | name: 'test', 93 | type: 'directory', 94 | shouldIgnore: () => true, 95 | }, 96 | ]); 97 | 98 | expect(fs.existsSync('zip-file.zip')).to.be.true; 99 | 100 | const outputFilePath = join('zip-file.zip'); 101 | const mapFiles = await getUnzipedFilesInMap(outputFilePath); 102 | 103 | expect(mapFiles.size).to.be.eq(0); 104 | }); 105 | 106 | it('when dir is empty', async () => { 107 | const zip = new FasterZip(); 108 | 109 | await zip.run(MockFsFactory.DIR_EMPTY, './zip-file.zip', [ 110 | { 111 | path: path.join(MockFsFactory.DIR_EMPTY, 'empty'), 112 | name: 'test', 113 | type: 'directory', 114 | }, 115 | ]); 116 | 117 | expect(fs.existsSync('zip-file.zip')).to.be.true; 118 | 119 | const outputFilePath = join('zip-file.zip'); 120 | const mapFiles = await getUnzipedFilesInMap(outputFilePath); 121 | 122 | expect(mapFiles.size).to.be.eq(0); 123 | }); 124 | }); 125 | 126 | describe('should handle correctly errors', () => { 127 | it('on fsreaddir passing directory', async () => { 128 | const zip = new FasterZip(); 129 | let error: Error | undefined; 130 | 131 | await zip 132 | .run(MockFsFactory.DIR_PROJECT, './output.zip', [ 133 | { 134 | path: './not-exist', 135 | name: 'test', 136 | type: 'directory', 137 | }, 138 | ]) 139 | .catch(err => { 140 | error = err; 141 | }); 142 | 143 | expect(error?.message).to.contain('ENOENT, no such file or directory'); 144 | }); 145 | 146 | it('on fsreaddir passing file', async () => { 147 | const zip = new FasterZip(); 148 | let error: Error | undefined; 149 | 150 | await zip 151 | .run(MockFsFactory.DIR_PROJECT, './output.zip', [ 152 | { 153 | path: './not-exist', 154 | name: 'test', 155 | type: 'directory', 156 | }, 157 | ]) 158 | .catch(err => { 159 | error = err; 160 | }); 161 | 162 | expect(error?.message).to.contain('ENOENT, no such file or directory'); 163 | }); 164 | 165 | it('on zip artifact when pass wrong type', async () => { 166 | const zip = new FasterZip(); 167 | let error: Error | undefined; 168 | 169 | await zip 170 | .run(MockFsFactory.DIR_PROJECT, './zip-file.zip', [ 171 | { 172 | path: path.join(MockFsFactory.DIR_PROJECT, 'package.json'), 173 | name: 'test', 174 | type: 'directory', 175 | }, 176 | ]) 177 | .catch(err => { 178 | error = err; 179 | }); 180 | 181 | expect(error).to.not.be.undefined; 182 | expect(error?.message).to.contain('ENOTDIR, not a directory'); 183 | }); 184 | 185 | it('when file is protected', async () => { 186 | const zip = new FasterZip(); 187 | let error: Error | undefined; 188 | 189 | await zip 190 | .run(MockFsFactory.DIR_WITH_PROTECTED_FILE, './zip-file.zip', [ 191 | { 192 | path: path.join( 193 | MockFsFactory.DIR_WITH_PROTECTED_FILE, 194 | 'protected.js', 195 | ), 196 | name: 'test', 197 | type: 'file', 198 | }, 199 | ]) 200 | .catch(err => { 201 | error = err; 202 | }); 203 | 204 | expect(error).to.not.be.undefined; 205 | expect(error?.message).to.contain('EACCES, permission denied'); 206 | }); 207 | 208 | it('when file is protected inside folder', async () => { 209 | const zip = new FasterZip(); 210 | let error: Error | undefined; 211 | 212 | await zip 213 | .run( 214 | MockFsFactory.DIR_WITH_PROTECTED_FILE_INSIDE_FOLDER, 215 | './zip-file.zip', 216 | [ 217 | { 218 | path: path.join( 219 | MockFsFactory.DIR_WITH_PROTECTED_FILE_INSIDE_FOLDER, 220 | 'protected', 221 | ), 222 | name: 'test', 223 | type: 'directory', 224 | }, 225 | ], 226 | ) 227 | .catch(err => { 228 | error = err; 229 | }); 230 | 231 | expect(error).to.not.be.undefined; 232 | expect(error?.message).to.contain('EACCES, permission denied'); 233 | }); 234 | 235 | it('when output folder is protected', async () => { 236 | const zip = new FasterZip(); 237 | let error: Error | undefined; 238 | 239 | await zip 240 | .run( 241 | MockFsFactory.DIR_PROJECT, 242 | join(MockFsFactory.DIR_OUTPUT_PROTECTED, './zip-file.zip'), 243 | [ 244 | { 245 | path: path.join(MockFsFactory.DIR_PROJECT, 'package.json'), 246 | name: 'test', 247 | type: 'file', 248 | }, 249 | ], 250 | ) 251 | .catch(err => { 252 | error = err; 253 | }); 254 | 255 | expect(error).to.not.be.undefined; 256 | expect(error?.message).to.contain('EACCES, permission denied'); 257 | }); 258 | 259 | it('when passing wrong remapping', async () => { 260 | const zip = new FasterZip(); 261 | let error: Error | undefined; 262 | 263 | await zip 264 | .run(MockFsFactory.DIR_PROJECT, './zip-file.zip', [ 265 | { 266 | path: path.join(MockFsFactory.DIR_PROJECT, 'package.json'), 267 | metadataPath: '../package.json', 268 | name: 'test', 269 | type: 'file', 270 | }, 271 | ]) 272 | .catch(err => { 273 | error = err; 274 | }); 275 | 276 | expect(error).to.not.be.undefined; 277 | expect(error?.message).to.contain('invalid relative path'); 278 | }); 279 | }); 280 | }); 281 | -------------------------------------------------------------------------------- /test/example/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-modules-packer-example", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "node-modules-packer-example", 9 | "version": "1.0.0", 10 | "hasInstallScript": true, 11 | "license": "ISC", 12 | "dependencies": { 13 | "@h4ad/serverless-adapter": "2.10.0", 14 | "is-date-object": "1.0.5" 15 | }, 16 | "devDependencies": { 17 | "is-string": "1.0.7" 18 | }, 19 | "peerDependencies": { 20 | "is-plain-object": "5.0.0", 21 | "kind-of": "6.0.3" 22 | }, 23 | "peerDependenciesMeta": { 24 | "kind-of": { 25 | "optional": true 26 | } 27 | } 28 | }, 29 | "node_modules/@h4ad/serverless-adapter": { 30 | "version": "2.10.0", 31 | "resolved": "https://registry.npmjs.org/@h4ad/serverless-adapter/-/serverless-adapter-2.10.0.tgz", 32 | "integrity": "sha512-/G3AtmCEZcvgTd+YruMZh31x33nzq2LQ6SwtxnWqCXDt42a7RszIhZrG+PaGEqUMAKRElrNnGFqYNMAB+QahWA==", 33 | "engines": { 34 | "node": ">=12.0" 35 | }, 36 | "peerDependencies": { 37 | "@azure/functions": ">= 2.0.0", 38 | "@deepkit/http": ">= 1.0.1-alpha.74", 39 | "@hapi/hapi": ">= 20.0.0", 40 | "@trpc/server": ">= 9.0.0", 41 | "@types/aws-lambda": ">= 8.10.92", 42 | "@types/express": ">= 4.15.4", 43 | "@types/hapi": ">= 18.0.7", 44 | "@types/koa": ">= 2.11.2", 45 | "express": ">= 4.15.4", 46 | "fastify": ">= 3.0.0", 47 | "fastify-3": "npm:fastify@3.29.0", 48 | "firebase-admin": ">= 8.0.0", 49 | "firebase-admin-8": "npm:firebase-admin@^8.0.0", 50 | "firebase-functions": ">= 3.0.0", 51 | "koa": ">= 2.5.1", 52 | "reflect-metadata": "^0.1.13" 53 | }, 54 | "peerDependenciesMeta": { 55 | "@azure/functions": { 56 | "optional": true 57 | }, 58 | "@deepkit/http": { 59 | "optional": true 60 | }, 61 | "@hapi/hapi": { 62 | "optional": true 63 | }, 64 | "@trpc/server": { 65 | "optional": true 66 | }, 67 | "@types/aws-lambda": { 68 | "optional": true 69 | }, 70 | "@types/express": { 71 | "optional": true 72 | }, 73 | "@types/hapi": { 74 | "optional": true 75 | }, 76 | "@types/koa": { 77 | "optional": true 78 | }, 79 | "express": { 80 | "optional": true 81 | }, 82 | "fastify": { 83 | "optional": true 84 | }, 85 | "fastify-3": { 86 | "optional": true 87 | }, 88 | "firebase-admin": { 89 | "optional": true 90 | }, 91 | "firebase-admin-8": { 92 | "optional": true 93 | }, 94 | "firebase-functions": { 95 | "optional": true 96 | }, 97 | "koa": { 98 | "optional": true 99 | } 100 | } 101 | }, 102 | "node_modules/has-symbols": { 103 | "version": "1.0.3", 104 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 105 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", 106 | "engines": { 107 | "node": ">= 0.4" 108 | }, 109 | "funding": { 110 | "url": "https://github.com/sponsors/ljharb" 111 | } 112 | }, 113 | "node_modules/has-tostringtag": { 114 | "version": "1.0.0", 115 | "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", 116 | "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", 117 | "dependencies": { 118 | "has-symbols": "^1.0.2" 119 | }, 120 | "engines": { 121 | "node": ">= 0.4" 122 | }, 123 | "funding": { 124 | "url": "https://github.com/sponsors/ljharb" 125 | } 126 | }, 127 | "node_modules/is-date-object": { 128 | "version": "1.0.5", 129 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", 130 | "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", 131 | "dependencies": { 132 | "has-tostringtag": "^1.0.0" 133 | }, 134 | "engines": { 135 | "node": ">= 0.4" 136 | }, 137 | "funding": { 138 | "url": "https://github.com/sponsors/ljharb" 139 | } 140 | }, 141 | "node_modules/is-plain-object": { 142 | "version": "5.0.0", 143 | "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", 144 | "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", 145 | "peer": true, 146 | "engines": { 147 | "node": ">=0.10.0" 148 | } 149 | }, 150 | "node_modules/is-string": { 151 | "version": "1.0.7", 152 | "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", 153 | "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", 154 | "dev": true, 155 | "dependencies": { 156 | "has-tostringtag": "^1.0.0" 157 | }, 158 | "engines": { 159 | "node": ">= 0.4" 160 | }, 161 | "funding": { 162 | "url": "https://github.com/sponsors/ljharb" 163 | } 164 | }, 165 | "node_modules/kind-of": { 166 | "version": "6.0.3", 167 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", 168 | "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", 169 | "optional": true, 170 | "peer": true, 171 | "engines": { 172 | "node": ">=0.10.0" 173 | } 174 | }, 175 | "node_modules/reflect-metadata": { 176 | "version": "0.1.13", 177 | "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", 178 | "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", 179 | "peer": true 180 | } 181 | }, 182 | "dependencies": { 183 | "@h4ad/serverless-adapter": { 184 | "version": "2.10.0", 185 | "resolved": "https://registry.npmjs.org/@h4ad/serverless-adapter/-/serverless-adapter-2.10.0.tgz", 186 | "integrity": "sha512-/G3AtmCEZcvgTd+YruMZh31x33nzq2LQ6SwtxnWqCXDt42a7RszIhZrG+PaGEqUMAKRElrNnGFqYNMAB+QahWA==", 187 | "requires": {} 188 | }, 189 | "has-symbols": { 190 | "version": "1.0.3", 191 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 192 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" 193 | }, 194 | "has-tostringtag": { 195 | "version": "1.0.0", 196 | "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", 197 | "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", 198 | "requires": { 199 | "has-symbols": "^1.0.2" 200 | } 201 | }, 202 | "is-date-object": { 203 | "version": "1.0.5", 204 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", 205 | "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", 206 | "requires": { 207 | "has-tostringtag": "^1.0.0" 208 | } 209 | }, 210 | "is-plain-object": { 211 | "version": "5.0.0", 212 | "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", 213 | "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", 214 | "peer": true 215 | }, 216 | "is-string": { 217 | "version": "1.0.7", 218 | "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", 219 | "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", 220 | "dev": true, 221 | "requires": { 222 | "has-tostringtag": "^1.0.0" 223 | } 224 | }, 225 | "kind-of": { 226 | "version": "6.0.3", 227 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", 228 | "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", 229 | "optional": true, 230 | "peer": true 231 | }, 232 | "reflect-metadata": { 233 | "version": "0.1.13", 234 | "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", 235 | "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", 236 | "peer": true 237 | } 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /test/mockfs/mockfs.factory.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'fs'; 2 | import { join, resolve } from 'path'; 3 | import mockfs from 'mock-fs'; 4 | 5 | const node_modules = () => { 6 | const baseNodePath = join(__dirname, '..', 'example', 'node_modules'); 7 | const readFile = file => readFileSync(join(baseNodePath, file)); 8 | 9 | return mockfs.directory({ 10 | items: { 11 | '.package-lock.json': mockfs.file({ 12 | content: '', 13 | }), 14 | 'has-symbols': mockfs.directory({ 15 | items: { 16 | 'CHANGELOG.md': mockfs.file({ 17 | content: readFile('has-symbols/CHANGELOG.md'), 18 | }), 19 | 'index.js': mockfs.file({ 20 | content: readFile('has-symbols/index.js'), 21 | }), 22 | LICENSE: mockfs.file({ 23 | content: readFile('has-symbols/LICENSE'), 24 | }), 25 | 'package.json': mockfs.file({ 26 | content: '{}', 27 | }), 28 | 'README.md': mockfs.file({ 29 | content: readFile('has-symbols/README.md'), 30 | }), 31 | 'shams.js': mockfs.file({ 32 | content: readFile('has-symbols/shams.js'), 33 | }), 34 | }, 35 | }), 36 | 'has-tostringtag': mockfs.directory({ 37 | items: { 38 | 'CHANGELOG.md': mockfs.file({ 39 | content: readFile('has-tostringtag/CHANGELOG.md'), 40 | }), 41 | 'index.js': mockfs.file({ 42 | content: readFile('has-tostringtag/index.js'), 43 | }), 44 | LICENSE: mockfs.file({ 45 | content: readFile('has-tostringtag/LICENSE'), 46 | }), 47 | 'package.json': mockfs.file({ 48 | content: '{}', 49 | }), 50 | 'README.md': mockfs.file({ 51 | content: readFile('has-tostringtag/README.md'), 52 | }), 53 | 'shams.js': mockfs.file({ 54 | content: readFile('has-tostringtag/shams.js'), 55 | }), 56 | }, 57 | }), 58 | 'is-date-object': mockfs.directory({ 59 | items: { 60 | 'CHANGELOG.md': mockfs.file({ 61 | content: '', 62 | }), 63 | 'index.js': mockfs.file({ 64 | content: '', 65 | }), 66 | LICENSE: mockfs.file({ 67 | content: '', 68 | }), 69 | 'package.json': mockfs.file({ 70 | content: '', 71 | }), 72 | 'README.md': mockfs.file({ 73 | content: '', 74 | }), 75 | }, 76 | }), 77 | 'is-plain-object': mockfs.directory({ 78 | items: { 79 | 'is-plain-object.d.ts': mockfs.file({ 80 | content: '', 81 | }), 82 | dist: mockfs.directory({ 83 | items: { 84 | 'is-plain-object.js': mockfs.file({ 85 | content: '', 86 | }), 87 | 'is-plain-object.mjs': mockfs.file({ 88 | content: '', 89 | }), 90 | }, 91 | }), 92 | LICENSE: mockfs.file({ 93 | content: '', 94 | }), 95 | 'package.json': mockfs.file({ 96 | content: '', 97 | }), 98 | 'README.md': mockfs.file({ 99 | content: '', 100 | }), 101 | }, 102 | }), 103 | 'is-string': mockfs.directory({ 104 | items: { 105 | 'CHANGELOG.md': mockfs.file({ 106 | content: readFile('is-string/CHANGELOG.md'), 107 | }), 108 | 'index.js': mockfs.file({ 109 | content: readFile('is-string/index.js'), 110 | }), 111 | LICENSE: mockfs.file({ 112 | content: readFile('is-string/LICENSE'), 113 | }), 114 | 'package.json': mockfs.file({ 115 | content: '{}', 116 | }), 117 | 'README.md': mockfs.file({ 118 | content: readFile('is-string/README.md'), 119 | }), 120 | }, 121 | }), 122 | 'kind-of': mockfs.directory({ 123 | items: { 124 | 'CHANGELOG.md': mockfs.file({ 125 | content: readFile('kind-of/CHANGELOG.md'), 126 | }), 127 | 'index.js': mockfs.file({ 128 | content: readFile('kind-of/index.js'), 129 | }), 130 | LICENSE: mockfs.file({ 131 | content: readFile('kind-of/LICENSE'), 132 | }), 133 | 'package.json': mockfs.file({ 134 | content: '{}', 135 | }), 136 | 'README.md': mockfs.file({ 137 | content: readFile('kind-of/README.md'), 138 | }), 139 | }, 140 | }), 141 | '@h4ad': mockfs.directory({ 142 | items: { 143 | 'serverless-adapter': mockfs.directory({ 144 | items: { 145 | 'README.md': mockfs.file({ 146 | content: readFile('@h4ad/serverless-adapter/README.md'), 147 | }), 148 | lib: mockfs.directory({ 149 | items: { 150 | 'index.js': mockfs.file({ 151 | content: readFile('@h4ad/serverless-adapter/lib/index.js'), 152 | }), 153 | }, 154 | }), 155 | LICENSE: mockfs.file({ 156 | content: readFile('@h4ad/serverless-adapter/LICENSE'), 157 | }), 158 | 'package.json': mockfs.file({ 159 | content: '{}', 160 | }), 161 | }, 162 | }), 163 | }, 164 | }), 165 | }, 166 | }); 167 | }; 168 | 169 | export default class MockFsFactory { 170 | static DIR_PROJECT = 'project'; 171 | static DIR_WITH_DEPLOY_FILE = 'with_deploy_file'; 172 | static DIR_NO_NODE_MODULES = 'no_node_modules'; 173 | static DIR_NO_PACKAGE_LOCK_FILE = 'no_package_lock_file'; 174 | static DIR_EMPTY = 'dir_empty'; 175 | static DIR_WITH_PROTECTED_FILE = 'dir_with_protected_file'; 176 | static DIR_WITH_PROTECTED_FILE_INSIDE_FOLDER = 177 | 'dir_with_protected_file_inside_folder'; 178 | static DIR_OUTPUT_PROTECTED = 'dir_output_protected'; 179 | 180 | static createMockFs() { 181 | const defaultLoadOptions = { 182 | lazy: true, 183 | recursive: true, 184 | }; 185 | 186 | mockfs({ 187 | 'package.json': mockfs.load( 188 | resolve(__dirname, '../../package.json'), 189 | defaultLoadOptions, 190 | ), 191 | 'tsconfig.json': mockfs.load( 192 | resolve(__dirname, '../../tsconfig.json'), 193 | defaultLoadOptions, 194 | ), 195 | node_modules: mockfs.directory({ 196 | items: { 197 | '@oclif': mockfs.load( 198 | resolve(__dirname, '../../node_modules/@oclif'), 199 | ), 200 | cardinal: mockfs.load( 201 | resolve(__dirname, '../../node_modules/cardinal'), 202 | ), 203 | redeyed: mockfs.load( 204 | resolve(__dirname, '../../node_modules/redeyed'), 205 | ), 206 | ansicolors: mockfs.load( 207 | resolve(__dirname, '../../node_modules/ansicolors'), 208 | ), 209 | esbuild: mockfs.load( 210 | resolve(__dirname, '../../node_modules/esbuild'), 211 | ), 212 | // to make esbuild work 213 | 'esbuild-linux-64': mockfs.load( 214 | resolve(__dirname, '../../node_modules/esbuild-linux-64'), 215 | ), 216 | }, 217 | }), 218 | src: mockfs.load(resolve(__dirname, '../../src'), defaultLoadOptions), 219 | [MockFsFactory.DIR_PROJECT]: { 220 | 'package.json': mockfs.load( 221 | resolve(__dirname, '../example/package.json'), 222 | defaultLoadOptions, 223 | ), 224 | '.gitignore': mockfs.load( 225 | resolve(__dirname, '../example/.gitignore'), 226 | defaultLoadOptions, 227 | ), 228 | 'package-lock.json': mockfs.load( 229 | resolve(__dirname, '../example/package-lock.json'), 230 | defaultLoadOptions, 231 | ), 232 | node_modules: node_modules(), 233 | src: mockfs.load( 234 | resolve(__dirname, '../example/src'), 235 | defaultLoadOptions, 236 | ), 237 | dist: mockfs.load( 238 | resolve(__dirname, '../example/dist'), 239 | defaultLoadOptions, 240 | ), 241 | }, 242 | [MockFsFactory.DIR_WITH_DEPLOY_FILE]: { 243 | 'package.json': mockfs.load( 244 | resolve(__dirname, '../example/package.json'), 245 | defaultLoadOptions, 246 | ), 247 | '.gitignore': mockfs.load( 248 | resolve(__dirname, '../example/.gitignore'), 249 | defaultLoadOptions, 250 | ), 251 | 'package-lock.json': mockfs.load( 252 | resolve(__dirname, '../example/package-lock.json'), 253 | defaultLoadOptions, 254 | ), 255 | 'deploy.zip': mockfs.file({ 256 | content: 'teste', 257 | }), 258 | node_modules: node_modules(), 259 | src: mockfs.load( 260 | resolve(__dirname, '../example/src'), 261 | defaultLoadOptions, 262 | ), 263 | dist: mockfs.load( 264 | resolve(__dirname, '../example/dist'), 265 | defaultLoadOptions, 266 | ), 267 | }, 268 | [MockFsFactory.DIR_NO_NODE_MODULES]: { 269 | 'package.json': mockfs.load( 270 | resolve(__dirname, '../example/package.json'), 271 | defaultLoadOptions, 272 | ), 273 | 'package-lock.json': mockfs.load( 274 | resolve(__dirname, '../example/package-lock.json'), 275 | defaultLoadOptions, 276 | ), 277 | src: mockfs.load( 278 | resolve(__dirname, '../example/src'), 279 | defaultLoadOptions, 280 | ), 281 | dist: mockfs.load( 282 | resolve(__dirname, '../example/dist'), 283 | defaultLoadOptions, 284 | ), 285 | }, 286 | [MockFsFactory.DIR_NO_PACKAGE_LOCK_FILE]: { 287 | 'package.json': mockfs.load( 288 | resolve(__dirname, '../example/package.json'), 289 | defaultLoadOptions, 290 | ), 291 | node_modules: node_modules(), 292 | src: mockfs.load( 293 | resolve(__dirname, '../example/src'), 294 | defaultLoadOptions, 295 | ), 296 | dist: mockfs.load( 297 | resolve(__dirname, '../example/dist'), 298 | defaultLoadOptions, 299 | ), 300 | }, 301 | [MockFsFactory.DIR_EMPTY]: { 302 | empty: mockfs.directory(), 303 | }, 304 | [MockFsFactory.DIR_WITH_PROTECTED_FILE]: { 305 | 'protected.js': mockfs.file({ 306 | content: 'protected', 307 | mode: 0, 308 | }), 309 | }, 310 | [MockFsFactory.DIR_WITH_PROTECTED_FILE_INSIDE_FOLDER]: { 311 | protected: mockfs.directory({ 312 | items: { 313 | 'protected.js': mockfs.file({ 314 | content: 'protected', 315 | mode: 0, 316 | }), 317 | }, 318 | }), 319 | }, 320 | [MockFsFactory.DIR_OUTPUT_PROTECTED]: mockfs.directory({ 321 | items: {}, 322 | mode: 0, 323 | }), 324 | }); 325 | } 326 | 327 | static resetMockFs() { 328 | mockfs.restore(); 329 | } 330 | } 331 | -------------------------------------------------------------------------------- /src/commands/run/index.ts: -------------------------------------------------------------------------------- 1 | //#region Imports 2 | 3 | import { join, relative, resolve } from 'path'; 4 | import { 5 | DependencyInfo, 6 | DependencyType, 7 | ExtractorContainer, 8 | NpmExtractor, 9 | } from '@h4ad/dependency-extractor'; 10 | import { Flags } from '@oclif/core'; 11 | import { LoadOptions } from '@oclif/core/lib/interfaces'; 12 | import esbuild from 'esbuild'; 13 | import rimraf from 'rimraf'; 14 | import CustomCommand from '../../common/custom-command'; 15 | import CustomError from '../../common/custom-error'; 16 | import { defaultIgnoredFileExtensions } from '../../common/extensions'; 17 | import { 18 | safeExistsSync, 19 | safeMkdirSync, 20 | safeReadFileSync, 21 | safeStatSync, 22 | } from '../../common/fs'; 23 | import { HeadlessOptions } from '../../common/headless'; 24 | import { OutputInfo } from '../../common/output-info'; 25 | import { FasterZip, TransformAsyncCode, ZipArtifact } from '../../common/zip'; 26 | 27 | //#endregion 28 | 29 | export default class Run extends CustomCommand { 30 | //#region Static Properties 31 | 32 | static description = 'Pack files and node dependencies to zip file.'; 33 | static examples = [ 34 | '<%= config.bin %> <%= command.id %> /project/path -i dist', 35 | ]; 36 | 37 | static args = [ 38 | { 39 | name: 'dir', 40 | description: 'Project root directory', 41 | required: false, 42 | default: './', 43 | }, 44 | ]; 45 | 46 | static flags = { 47 | include: Flags.string({ 48 | char: 'i', 49 | description: 'Include more files during packing (eg: -i dist).', 50 | helpValue: 'package.json', 51 | multiple: true, 52 | required: false, 53 | }), 54 | 'ignore-file-ext': Flags.string({ 55 | char: 'e', 56 | description: 'Force ignore specific file extension.', 57 | multiple: true, 58 | required: false, 59 | }), 60 | 'disable-default-ignore-file-ext': Flags.boolean({ 61 | description: 62 | 'Disable including default ignored extensions that we consider as useless.', 63 | required: false, 64 | default: false, 65 | allowNo: true, 66 | }), 67 | 'include-node-path': Flags.string({ 68 | description: 69 | 'Force include folders starting with the specified path (eg --include-node-path "dev-dependency" will include node_modules/dev-dependency), but you need to MANUALLY add your sub-dependencies if dev-dependency has production dependencies.', 70 | helpValue: 'dev-dependency', 71 | multiple: true, 72 | required: false, 73 | }), 74 | 'ignore-node-path': Flags.string({ 75 | description: 76 | 'Force exclude folders starting with specified path (eg: -n "typeorm/browser" will exclude node_modules/typeorm/browser).', 77 | helpValue: 'typeorm/browser', 78 | multiple: true, 79 | required: false, 80 | }), 81 | prod: Flags.boolean({ 82 | description: 83 | 'Include production dependencies when pack node dependencies.', 84 | default: true, 85 | required: false, 86 | allowNo: true, 87 | }), 88 | peer: Flags.boolean({ 89 | description: 'Include peer dependencies when pack node dependencies.', 90 | default: false, 91 | required: false, 92 | allowNo: true, 93 | }), 94 | dev: Flags.boolean({ 95 | description: 96 | 'Include development dependencies when pack node dependencies.', 97 | default: false, 98 | required: false, 99 | allowNo: true, 100 | }), 101 | optional: Flags.boolean({ 102 | description: 'Include optional dependencies when pack node dependencies.', 103 | default: false, 104 | required: false, 105 | allowNo: true, 106 | }), 107 | 'output-path': Flags.string({ 108 | description: 'Specify output path for the zip file.', 109 | default: './', 110 | required: false, 111 | }), 112 | 'output-file': Flags.string({ 113 | description: 'Specify output file name for the zip file.', 114 | default: 'deploy.zip', 115 | required: false, 116 | }), 117 | minify: Flags.boolean({ 118 | description: 'Minify each .js file with esbuild.', 119 | default: false, 120 | required: false, 121 | allowNo: true, 122 | }), 123 | 'minify-keep-names': Flags.boolean({ 124 | description: 'Keep the names during minification.', 125 | default: false, 126 | required: false, 127 | allowNo: true, 128 | }), 129 | quiet: Flags.boolean({ 130 | char: 'q', 131 | description: 'Run without logging.', 132 | default: false, 133 | required: false, 134 | }), 135 | }; 136 | 137 | //#endregion 138 | 139 | //#region Static Methods 140 | 141 | public static async headless( 142 | options: HeadlessOptions, 143 | loadOptions?: LoadOptions, 144 | ): Promise { 145 | const args: string[] = [options.dir]; 146 | 147 | const pushFlagWithArguments = (flag: string, options: string[]) => { 148 | for (const option of options) { 149 | args.push(flag); 150 | args.push(option); 151 | } 152 | }; 153 | 154 | const pushFlagBoolean = (flag: string, cond: boolean) => { 155 | const prefix = cond ? '--' : '--no-'; 156 | 157 | args.push(`${prefix}${flag}`); 158 | }; 159 | 160 | if (Array.isArray(options.include)) 161 | pushFlagWithArguments('-i', options.include); 162 | 163 | if (Array.isArray(options.ignoreFileExt)) 164 | pushFlagWithArguments('-e', options.ignoreFileExt); 165 | 166 | if (options.disableDefaultIgnoreFileExt !== undefined) { 167 | pushFlagBoolean( 168 | 'disable-default-ignore-file-ext', 169 | options.disableDefaultIgnoreFileExt, 170 | ); 171 | } 172 | 173 | if (Array.isArray(options.includeNodePath)) 174 | pushFlagWithArguments('--include-node-path', options.includeNodePath); 175 | 176 | if (Array.isArray(options.ignoreNodePath)) 177 | pushFlagWithArguments('--ignore-node-path', options.ignoreNodePath); 178 | 179 | if (options.prod !== undefined) pushFlagBoolean('prod', options.prod); 180 | 181 | if (options.dev !== undefined) pushFlagBoolean('dev', options.dev); 182 | 183 | if (options.peer !== undefined) pushFlagBoolean('peer', options.peer); 184 | 185 | if (options.optional !== undefined) 186 | pushFlagBoolean('optional', options.optional); 187 | 188 | if (options.outputPath !== undefined) 189 | args.push('--output-path', options.outputPath); 190 | 191 | if (options.outputFile !== undefined) 192 | args.push('--output-file', options.outputFile); 193 | 194 | if (options.minify !== undefined) pushFlagBoolean('minify', options.minify); 195 | 196 | if (options.minifyKeepNames !== undefined) 197 | pushFlagBoolean('minify-keep-names', options.minifyKeepNames); 198 | 199 | return await this.run(args, loadOptions); 200 | } 201 | 202 | //#endregion 203 | 204 | //#region Public Methods 205 | 206 | public async run(): Promise { 207 | const { args, flags } = await this.parse(Run); 208 | 209 | const dir = args['dir']; 210 | const outputPath = flags['output-path']; 211 | const outputFile = flags['output-file']; 212 | 213 | this.checkForNodeModules(flags, dir); 214 | this.checkForNoOutputFile(flags, dir, outputPath, outputFile); 215 | 216 | const ignoredFileExtensions = this.getIgnoredFileExtensions(flags); 217 | const ignoredNodePaths = this.getIgnoredNodePaths(flags); 218 | const includedNodePaths = this.getIncludedNodePaths(flags); 219 | const selectedDependencies = this.getSelectedDependencies(flags, dir); 220 | 221 | const shouldIgnoreNodeFile = this.getShouldIgnoreNodeFileCallback( 222 | dir, 223 | ignoredFileExtensions, 224 | ignoredNodePaths, 225 | includedNodePaths, 226 | selectedDependencies, 227 | ); 228 | 229 | const zipArtifacts: ZipArtifact[] = this.getZipArtifacts( 230 | dir, 231 | flags, 232 | shouldIgnoreNodeFile, 233 | ); 234 | 235 | const outputFilePath = resolve(dir, outputPath, outputFile); 236 | 237 | await this.zipDirectory(flags, dir, zipArtifacts, outputFilePath); 238 | 239 | const size = safeStatSync(outputFilePath).size; 240 | 241 | return { size, file: outputFile, path: outputPath }; 242 | } 243 | 244 | //#endregion 245 | 246 | //#region Protected Methods 247 | 248 | protected checkForNodeModules(flags: typeof Run.flags, dir: string): void { 249 | this.logMessage(flags, 'log', 'Checking node folder'); 250 | 251 | const nodeModulesFolderPath = join(dir, 'node_modules'); 252 | 253 | if (safeExistsSync(nodeModulesFolderPath)) { 254 | this.logMessage(flags, 'log', 'Checking node folder... found'); 255 | return; 256 | } 257 | 258 | throw new CustomError( 259 | `Invalid Node Modules: folder ${nodeModulesFolderPath} does not exist`, 260 | { 261 | code: 'ERR_NODE_MODULES_NOT_FOUND', 262 | suggestions: [`Maybe you forgot to run "npm i" on ${dir}?`], 263 | }, 264 | ); 265 | } 266 | 267 | protected checkForNoOutputFile( 268 | flags: typeof Run.flags, 269 | dir: string, 270 | outputPath: string, 271 | outputFile: string, 272 | ): void { 273 | this.logMessage(flags, 'log', 'Removing old output file'); 274 | 275 | const resolvedOutputPath = resolve(dir, outputPath); 276 | const outputFilePath = join(resolvedOutputPath, outputFile); 277 | 278 | if (!safeExistsSync(resolvedOutputPath)) { 279 | this.logMessage( 280 | flags, 281 | 'log', 282 | `Not found folder in ${outputPath}, creating new one...`, 283 | ); 284 | 285 | safeMkdirSync(resolvedOutputPath, { recursive: true }); 286 | } 287 | 288 | if (!safeExistsSync(outputFilePath)) { 289 | this.logMessage( 290 | flags, 291 | 'debug', 292 | `Not found ${outputFilePath} in output folder, it's not necessary remove.`, 293 | ); 294 | 295 | this.logMessage(flags, 'log', 'Removing old output file... done'); 296 | 297 | return; 298 | } 299 | 300 | this.logMessage( 301 | flags, 302 | 'log', 303 | `File found at ${outputFilePath}, removing...`, 304 | ); 305 | rimraf.sync(outputFilePath); 306 | this.logMessage(flags, 'log', `File found at ${outputFilePath}, removed.`); 307 | 308 | this.logMessage(flags, 'log', 'Removing old output file... done'); 309 | } 310 | 311 | protected getSelectedDependencies( 312 | flags: typeof Run.flags, 313 | dir: string, 314 | ): DependencyInfo[] { 315 | this.logMessage(flags, 'log', 'Getting selected dependencies'); 316 | 317 | const dependenciesContainer = this.getExtractedDependenciesFromLockFile( 318 | flags, 319 | dir, 320 | ); 321 | 322 | const allDependencies = dependenciesContainer.getAllDependencies(); 323 | const hasFlag = (type: number, flag: DependencyType) => (type & flag) > 0; 324 | 325 | const selectedDependencies = allDependencies.filter(dependency => { 326 | if ( 327 | flags.prod && 328 | hasFlag(dependency.type, DependencyType.PRODUCTION) && 329 | !hasFlag(dependency.type, DependencyType.PEER) 330 | ) 331 | return true; 332 | 333 | if (flags.dev && hasFlag(dependency.type, DependencyType.DEVELOPMENT)) 334 | return true; 335 | 336 | if (flags.peer && hasFlag(dependency.type, DependencyType.PEER)) 337 | return true; 338 | 339 | if (flags.optional && hasFlag(dependency.type, DependencyType.OPTIONAL)) 340 | return true; 341 | 342 | return false; 343 | }); 344 | 345 | this.logMessage( 346 | flags, 347 | 'log', 348 | `Total dependencies found: ${allDependencies.length}.`, 349 | ); 350 | this.logMessage( 351 | flags, 352 | 'log', 353 | `Selected dependencies: ${selectedDependencies.length}.`, 354 | ); 355 | 356 | return selectedDependencies; 357 | } 358 | 359 | protected getExtractedDependenciesFromLockFile( 360 | flags: typeof Run.flags, 361 | dir: string, 362 | ): ExtractorContainer { 363 | const packageLockFilePath = join(dir, 'package-lock.json'); 364 | 365 | if (safeExistsSync(packageLockFilePath)) { 366 | this.logMessage( 367 | flags, 368 | 'debug', 369 | `Found lock file in ${packageLockFilePath}.`, 370 | ); 371 | this.logMessage( 372 | flags, 373 | 'debug', 374 | 'Reading and parsing lock file with NpmExtractor.', 375 | ); 376 | 377 | return new NpmExtractor().parse( 378 | safeReadFileSync(packageLockFilePath).toString('utf-8'), 379 | ); 380 | } 381 | 382 | throw new CustomError( 383 | `Invalid package-lock.json: file ${packageLockFilePath} does not exist`, 384 | { 385 | code: 'ERR_LOCK_FILE', 386 | suggestions: [ 387 | 'Currently we only support package-lock.json to detect production dependencies.', 388 | ], 389 | }, 390 | ); 391 | } 392 | 393 | protected getIgnoredFileExtensions(flags: typeof Run.flags): string[] { 394 | const ignoredFileExtensions: string[] = []; 395 | 396 | if (!flags['disable-default-ignore-file-ext']) 397 | ignoredFileExtensions.push(...defaultIgnoredFileExtensions); 398 | 399 | if (Array.isArray(flags['ignore-file-ext'])) 400 | ignoredFileExtensions.push(...flags['ignore-file-ext']); 401 | 402 | this.logMessage(flags, 'debug', 'Using following ignored file extensions:'); 403 | this.logMessage(flags, 'debug', JSON.stringify(ignoredFileExtensions)); 404 | 405 | return ignoredFileExtensions; 406 | } 407 | 408 | protected getIgnoredNodePaths(flags: typeof Run.flags): string[] { 409 | const ignoredNodePaths: string[] = []; 410 | 411 | if (Array.isArray(flags['ignore-node-path'])) 412 | ignoredNodePaths.push(...flags['ignore-node-path']); 413 | 414 | this.logMessage(flags, 'debug', 'Using following ignored node paths:'); 415 | this.logMessage(flags, 'debug', JSON.stringify(ignoredNodePaths)); 416 | 417 | return ignoredNodePaths.map(this.fixPath.bind(this)); 418 | } 419 | 420 | protected getIncludedNodePaths(flags: typeof Run.flags): string[] { 421 | const includedNodePaths: string[] = []; 422 | 423 | if (Array.isArray(flags['include-node-path'])) 424 | includedNodePaths.push(...flags['include-node-path']); 425 | 426 | this.logMessage(flags, 'debug', 'Using following included node paths:'); 427 | this.logMessage(flags, 'debug', JSON.stringify(includedNodePaths)); 428 | 429 | return includedNodePaths.map(this.fixPath.bind(this)); 430 | } 431 | 432 | protected getShouldIgnoreNodeFileCallback( 433 | dir: string, 434 | ignoredFileExtensions: string[], 435 | ignoredNodePaths: string[], 436 | includedNodePaths: string[], 437 | selectedDependencies: DependencyInfo[], 438 | ): (filename: string) => boolean { 439 | return filename => { 440 | if (ignoredFileExtensions.some(ext => filename.endsWith(ext))) 441 | return true; 442 | 443 | const filenameNodePath = this.fixPath( 444 | relative(resolve(dir, 'node_modules'), filename), 445 | ); 446 | 447 | if (includedNodePaths.some(path => filenameNodePath.startsWith(path))) 448 | return false; 449 | 450 | if (ignoredNodePaths.some(path => filenameNodePath.startsWith(path))) 451 | return true; 452 | 453 | const isSelectedDependency = selectedDependencies.some(dependency => 454 | filenameNodePath.startsWith(dependency.name), 455 | ); 456 | 457 | return !isSelectedDependency; 458 | }; 459 | } 460 | 461 | protected getZipArtifacts( 462 | dir: string, 463 | flags: typeof Run.flags, 464 | shouldIgnoreNodeFile: (filename: string) => boolean, 465 | ): ZipArtifact[] { 466 | this.logMessage(flags, 'log', 'Getting artifacts to zip'); 467 | 468 | const isJsFileRegex = /(\.js|\.cjs|\.mjs)$/; 469 | 470 | const keepNames = !!flags['minify-keep-names']; 471 | 472 | const transformAsyncCode: TransformAsyncCode = (code: Uint8Array) => 473 | esbuild 474 | .transform(code, { minify: true, keepNames }) 475 | .then(result => result.code); 476 | 477 | const transformerFunction: ZipArtifact['transformer'] = ( 478 | filePath, 479 | metadataPath, 480 | ) => { 481 | const isJsFile = 482 | isJsFileRegex.test(filePath) || isJsFileRegex.test(metadataPath); 483 | 484 | if (!isJsFile) return undefined; 485 | 486 | return transformAsyncCode; 487 | }; 488 | 489 | const transformer = flags.minify ? transformerFunction : undefined; 490 | 491 | const artifacts: ZipArtifact[] = [ 492 | { 493 | path: join(dir, 'node_modules'), 494 | name: 'node_modules', 495 | type: 'directory', 496 | transformer, 497 | shouldIgnore: shouldIgnoreNodeFile, 498 | }, 499 | ]; 500 | 501 | const includeFiles = Array.isArray(flags.include) ? flags.include : []; 502 | 503 | for (const includeFile of includeFiles) { 504 | const [relativePath, pathMappedTo] = includeFile.split(':'); 505 | 506 | const includeFilePath = join(dir, relativePath); 507 | const stats = safeStatSync(includeFilePath); 508 | const metadataPath = pathMappedTo 509 | ? resolve('/', pathMappedTo).slice(1) 510 | : undefined; 511 | 512 | const type = stats.isDirectory() ? 'directory' : 'file'; 513 | 514 | artifacts.push({ 515 | path: this.fixPath(includeFilePath), 516 | name: this.fixPath(includeFile), 517 | metadataPath: metadataPath ? this.fixPath(metadataPath) : metadataPath, 518 | transformer, 519 | type, 520 | }); 521 | } 522 | 523 | this.logMessage( 524 | flags, 525 | 'log', 526 | `Getting artifacts to zip... ${artifacts.length} selected`, 527 | ); 528 | 529 | return artifacts; 530 | } 531 | 532 | protected async zipDirectory( 533 | flags: typeof Run.flags, 534 | dir: string, 535 | zipArtifacts: ZipArtifact[], 536 | outputPath: string, 537 | ): Promise { 538 | this.logMessage(flags, 'log', 'Creating the output file'); 539 | 540 | if (!outputPath.endsWith('.zip')) { 541 | throw new CustomError('Invalid output file extension.', { 542 | code: 'ERR_OUTPUT_FILE', 543 | suggestions: [ 544 | 'You should specific an --output-file with .zip extension.', 545 | ], 546 | }); 547 | } 548 | 549 | const rootPath = resolve(process.cwd(), dir); 550 | const fasterZip = new FasterZip(); 551 | 552 | await fasterZip.run(rootPath, outputPath, zipArtifacts); 553 | 554 | this.logMessage(flags, 'log', 'Creating the output file... created'); 555 | } 556 | 557 | protected logMessage( 558 | flags: typeof Run.flags, 559 | type: 'log' | 'debug', 560 | message?: string, 561 | ...args: any[] 562 | ): void { 563 | if (flags.quiet) return; 564 | 565 | this[type](message, args); 566 | } 567 | 568 | protected fixPath(path: string): string { 569 | return path.replace(/\\\\/g, '/').replace(/\\/g, '/'); 570 | } 571 | 572 | //#endregion 573 | } 574 | -------------------------------------------------------------------------------- /test/commands/run/index.test.ts: -------------------------------------------------------------------------------- 1 | import { existsSync, statSync } from 'fs'; 2 | import { join } from 'path'; 3 | import { expect } from '@oclif/test'; 4 | import Run from '../../../src/commands/run'; 5 | import MockFsFactory from '../../mockfs/mockfs.factory'; 6 | import { fsTest } from '../../mockfs/test'; 7 | import { getUnzipedFilesInMap } from '../../mockfs/unzip'; 8 | 9 | const dependenciesWithPeerAndDevSize = 5787; 10 | const minifyDependenciesSize = 5043; 11 | const minifyDependenciesWithKeepNamesSize = 5464; 12 | 13 | describe('when pack is called', () => { 14 | describe('with only project folder', () => { 15 | fsTest 16 | .stdout() 17 | .stderr() 18 | .fsmockCommand(['run', MockFsFactory.DIR_PROJECT]) 19 | .it( 20 | 'should generate deploy.zip with only production dependencies', 21 | async ctx => { 22 | expect(ctx.stderr).to.be.empty; 23 | 24 | const outputFilePath = join(MockFsFactory.DIR_PROJECT, 'deploy.zip'); 25 | const createdTheDeployZip = existsSync(outputFilePath); 26 | 27 | expect(createdTheDeployZip).to.be.eq(true); 28 | 29 | const mapFiles = await getUnzipedFilesInMap(outputFilePath); 30 | const mapFileKeys = [...mapFiles.keys()]; 31 | 32 | expect( 33 | mapFileKeys.every( 34 | mapFile => 35 | !mapFile.startsWith('node_modules/is-string') && 36 | !mapFile.startsWith('node_modules/kind-of') && 37 | !mapFile.startsWith('node_modules/is-plain-object'), 38 | ), 39 | ).to.be.true; 40 | }, 41 | ); 42 | 43 | fsTest 44 | .stdout() 45 | .stderr() 46 | .fsmockCommand(['run', '--dev', MockFsFactory.DIR_PROJECT]) 47 | .it('should generate deploy.zip with dev dependencies', async ctx => { 48 | expect(ctx.stderr).to.be.empty; 49 | 50 | const outputFilePath = join(MockFsFactory.DIR_PROJECT, 'deploy.zip'); 51 | const createdTheDeployZip = existsSync(outputFilePath); 52 | 53 | expect(createdTheDeployZip).to.be.eq(true); 54 | 55 | const mapFiles = await getUnzipedFilesInMap(outputFilePath); 56 | const mapFileKeys = [...mapFiles.keys()]; 57 | 58 | expect( 59 | mapFileKeys.every( 60 | mapFile => 61 | !mapFile.startsWith('node_modules/kind-of') && 62 | !mapFile.startsWith('node_modules/is-plain-object'), 63 | ), 64 | ).to.be.true; 65 | }); 66 | 67 | fsTest 68 | .stdout() 69 | .stderr() 70 | .fsmockCommand(['run', '--peer', MockFsFactory.DIR_PROJECT]) 71 | .it('should generate deploy.zip with peer dependencies', async ctx => { 72 | expect(ctx.stderr).to.be.empty; 73 | 74 | const outputFilePath = join(MockFsFactory.DIR_PROJECT, 'deploy.zip'); 75 | const createdTheDeployZip = existsSync(outputFilePath); 76 | 77 | expect(createdTheDeployZip).to.be.eq(true); 78 | 79 | const mapFiles = await getUnzipedFilesInMap(outputFilePath); 80 | const mapFileKeys = [...mapFiles.keys()]; 81 | 82 | expect( 83 | mapFileKeys.every( 84 | mapFile => !mapFile.startsWith('node_modules/is-string'), 85 | ), 86 | ).to.be.true; 87 | }); 88 | 89 | fsTest 90 | .stdout() 91 | .stderr() 92 | .fsmockCommand(['run', '--optional', MockFsFactory.DIR_PROJECT]) 93 | .it( 94 | 'should generate deploy.zip with optional dependencies', 95 | async ctx => { 96 | expect(ctx.stderr).to.be.empty; 97 | 98 | const outputFilePath = join(MockFsFactory.DIR_PROJECT, 'deploy.zip'); 99 | const createdTheDeployZip = existsSync(outputFilePath); 100 | 101 | expect(createdTheDeployZip).to.be.eq(true); 102 | 103 | const mapFiles = await getUnzipedFilesInMap(outputFilePath); 104 | const mapFileKeys = [...mapFiles.keys()]; 105 | 106 | expect( 107 | mapFileKeys.every( 108 | mapFile => 109 | !mapFile.startsWith('node_modules/is-string') && 110 | !mapFile.startsWith('node_modules/is-plain-object'), 111 | ), 112 | ).to.be.true; 113 | }, 114 | ); 115 | }); 116 | 117 | describe('with --json flag', () => { 118 | fsTest 119 | .stdout() 120 | .stderr() 121 | .fsmockCommand(['run', '--json', MockFsFactory.DIR_PROJECT]) 122 | .it('should output success in json', ctx => { 123 | expect(ctx.stdout).to.contain('"status": "success"'); 124 | expect(ctx.stdout).to.contain('"size":'); 125 | expect(ctx.stdout).to.contain('"path": "'); 126 | expect(ctx.stdout).to.contain('"file": "deploy.zip"'); 127 | 128 | const createdTheDeployZip = existsSync( 129 | join(MockFsFactory.DIR_PROJECT, 'deploy.zip'), 130 | ); 131 | 132 | expect(createdTheDeployZip).to.be.eq(true); 133 | }); 134 | 135 | fsTest 136 | .stdout() 137 | .stderr() 138 | .fsmockCommand(['run', '--json', MockFsFactory.DIR_NO_NODE_MODULES]) 139 | .it('should output error in json', ctx => { 140 | expect(ctx.stdout).to.contain('"status": "error"'); 141 | expect(ctx.stdout).to.contain('"message": "'); 142 | expect(ctx.stdout).to.contain('"code": "'); 143 | expect(ctx.stdout).to.contain('"sugestions": ['); 144 | }); 145 | }); 146 | 147 | describe('with --include flag', () => { 148 | fsTest 149 | .stdout() 150 | .stderr() 151 | .fsmockCommand([ 152 | 'run', 153 | MockFsFactory.DIR_PROJECT, 154 | '-i', 155 | './src', 156 | '-i', 157 | '.gitignore', 158 | ]) 159 | .it('should generate deploy.zip with included files', async ctx => { 160 | expect(ctx.stderr).to.be.empty; 161 | 162 | const outputFilePath = join(MockFsFactory.DIR_PROJECT, 'deploy.zip'); 163 | const createdTheDeployZip = existsSync(outputFilePath); 164 | 165 | expect(createdTheDeployZip).to.be.eq(true); 166 | 167 | const mapFiles = await getUnzipedFilesInMap(outputFilePath); 168 | 169 | expect(mapFiles.has('src/index.js')).to.be.true; 170 | expect(mapFiles.has('.gitignore')).to.be.true; 171 | }); 172 | 173 | fsTest 174 | .stdout() 175 | .stderr() 176 | .fsmockCommand([ 177 | 'run', 178 | MockFsFactory.DIR_PROJECT, 179 | '-i', 180 | './src:dist/src', 181 | '-i', 182 | '.gitignore:dist/.gitignore', 183 | ]) 184 | .it('should generate deploy.zip with included files', async ctx => { 185 | expect(ctx.stderr).to.be.empty; 186 | 187 | const outputFilePath = join(MockFsFactory.DIR_PROJECT, 'deploy.zip'); 188 | const createdTheDeployZip = existsSync(outputFilePath); 189 | 190 | expect(createdTheDeployZip).to.be.eq(true); 191 | 192 | const mapFiles = await getUnzipedFilesInMap(outputFilePath); 193 | 194 | expect(mapFiles.has('dist/src/index.js')).to.be.true; 195 | expect(mapFiles.has('dist/.gitignore')).to.be.true; 196 | }); 197 | 198 | fsTest 199 | .stdout() 200 | .stderr() 201 | .fsmockCommand([ 202 | 'run', 203 | MockFsFactory.DIR_PROJECT, 204 | '-i', 205 | './src:../../dist/src', 206 | '-i', 207 | '.gitignore:../dist/.gitignore', 208 | ]) 209 | .it( 210 | 'should ignore when relative is pointing out of zip base path', 211 | async ctx => { 212 | expect(ctx.stderr).to.be.empty; 213 | 214 | const outputFilePath = join(MockFsFactory.DIR_PROJECT, 'deploy.zip'); 215 | const createdTheDeployZip = existsSync(outputFilePath); 216 | 217 | expect(createdTheDeployZip).to.be.eq(true); 218 | 219 | const mapFiles = await getUnzipedFilesInMap(outputFilePath); 220 | 221 | expect(mapFiles.has('dist/src/index.js')).to.be.true; 222 | expect(mapFiles.has('dist/.gitignore')).to.be.true; 223 | }, 224 | ); 225 | 226 | fsTest 227 | .stdout() 228 | .stderr() 229 | .fsmockCommand([ 230 | 'run', 231 | MockFsFactory.DIR_PROJECT, 232 | '-i', 233 | 'dont-exist.test', 234 | ]) 235 | .catch(err => { 236 | expect(err.message).to.contain('ENOENT: no such file'); 237 | }) 238 | .it('should throw error when include file that not exist'); 239 | }); 240 | 241 | describe('with --ignore-file-ext flag', () => { 242 | fsTest 243 | .stdout() 244 | .stderr() 245 | .fsmockCommand(['run', MockFsFactory.DIR_PROJECT]) 246 | .it('should have json files inside node_modules', async ctx => { 247 | expect(ctx.stderr).to.be.empty; 248 | 249 | const outputFilePath = join(MockFsFactory.DIR_PROJECT, 'deploy.zip'); 250 | const createdTheDeployZip = existsSync(outputFilePath); 251 | 252 | expect(createdTheDeployZip).to.be.eq(true); 253 | 254 | const mapFiles = await getUnzipedFilesInMap(outputFilePath); 255 | 256 | expect( 257 | mapFiles.has('node_modules/@h4ad/serverless-adapter/package.json'), 258 | ).to.be.true; 259 | expect(mapFiles.has('node_modules/is-date-object/package.json')).to.be 260 | .true; 261 | }); 262 | 263 | fsTest 264 | .stdout() 265 | .stderr() 266 | .fsmockCommand(['run', MockFsFactory.DIR_PROJECT, '-e', '.json']) 267 | .it( 268 | 'should generate deploy.zip without excluded extension files', 269 | async ctx => { 270 | expect(ctx.stderr).to.be.empty; 271 | 272 | const outputFilePath = join(MockFsFactory.DIR_PROJECT, 'deploy.zip'); 273 | const mapFiles = await getUnzipedFilesInMap(outputFilePath); 274 | 275 | expect( 276 | mapFiles.has('node_modules/@h4ad/serverless-adapter/package.json'), 277 | ).to.be.not.be.true; 278 | expect(mapFiles.has('node_modules/is-date-object/package.json')).to 279 | .not.be.true; 280 | }, 281 | ); 282 | }); 283 | 284 | describe('with --disable-default-ignore-file-ext flag', () => { 285 | fsTest 286 | .stdout() 287 | .stderr() 288 | .fsmockCommand(['run', MockFsFactory.DIR_PROJECT]) 289 | .it( 290 | 'should generate zip excluding default extensions files', 291 | async ctx => { 292 | expect(ctx.stderr).to.be.empty; 293 | 294 | const outputFilePath = join(MockFsFactory.DIR_PROJECT, 'deploy.zip'); 295 | const mapFiles = await getUnzipedFilesInMap(outputFilePath); 296 | 297 | expect( 298 | mapFiles.has('node_modules/@h4ad/serverless-adapter/README.md'), 299 | ).to.be.false; 300 | expect(mapFiles.has('node_modules/is-date-object/README.md')).to.be 301 | .false; 302 | }, 303 | ); 304 | 305 | fsTest 306 | .stdout() 307 | .stderr() 308 | .fsmockCommand([ 309 | 'run', 310 | MockFsFactory.DIR_PROJECT, 311 | '--disable-default-ignore-file-ext', 312 | ]) 313 | .it( 314 | 'should generate deploy.zip without default excluded extension files', 315 | async ctx => { 316 | expect(ctx.stderr).to.be.empty; 317 | 318 | const outputFilePath = join(MockFsFactory.DIR_PROJECT, 'deploy.zip'); 319 | const mapFiles = await getUnzipedFilesInMap(outputFilePath); 320 | 321 | expect( 322 | mapFiles.has('node_modules/@h4ad/serverless-adapter/README.md'), 323 | ).to.be.true; 324 | expect(mapFiles.has('node_modules/is-date-object/README.md')).to.be 325 | .true; 326 | }, 327 | ); 328 | }); 329 | 330 | describe('with --include-node-path flag', () => { 331 | fsTest 332 | .stdout() 333 | .stderr() 334 | .fsmockCommand(['run', MockFsFactory.DIR_PROJECT]) 335 | .it('should generate zip including without kind-of', async ctx => { 336 | expect(ctx.stderr).to.be.empty; 337 | 338 | const outputFilePath = join(MockFsFactory.DIR_PROJECT, 'deploy.zip'); 339 | const mapFiles = await getUnzipedFilesInMap(outputFilePath); 340 | const mapFileKeys = [...mapFiles.keys()]; 341 | 342 | expect( 343 | mapFileKeys.every( 344 | mapFile => !mapFile.startsWith('node_modules/kind-of'), 345 | ), 346 | ).to.be.true; 347 | }); 348 | 349 | fsTest 350 | .stdout() 351 | .stderr() 352 | .fsmockCommand([ 353 | 'run', 354 | MockFsFactory.DIR_PROJECT, 355 | '--include-node-path', 356 | 'kind-of', 357 | ]) 358 | .it( 359 | 'should generate deploy.zip with kind-of dependency included', 360 | async ctx => { 361 | expect(ctx.stderr).to.be.empty; 362 | 363 | const outputFilePath = join(MockFsFactory.DIR_PROJECT, 'deploy.zip'); 364 | const mapFiles = await getUnzipedFilesInMap(outputFilePath); 365 | const mapFileKeys = [...mapFiles.keys()]; 366 | 367 | expect( 368 | mapFileKeys.some(mapFile => 369 | mapFile.startsWith('node_modules/kind-of'), 370 | ), 371 | ).to.be.true; 372 | }, 373 | ); 374 | }); 375 | 376 | describe('with --ignore-node-path flag', () => { 377 | fsTest 378 | .stdout() 379 | .stderr() 380 | .fsmockCommand(['run', MockFsFactory.DIR_PROJECT]) 381 | .it('should generate zip including has-symbols', async ctx => { 382 | expect(ctx.stderr).to.be.empty; 383 | 384 | const outputFilePath = join(MockFsFactory.DIR_PROJECT, 'deploy.zip'); 385 | const mapFiles = await getUnzipedFilesInMap(outputFilePath); 386 | const mapFileKeys = [...mapFiles.keys()]; 387 | 388 | expect( 389 | mapFileKeys.some(mapFile => 390 | mapFile.startsWith('node_modules/has-symbols'), 391 | ), 392 | ).to.be.true; 393 | }); 394 | 395 | fsTest 396 | .stdout() 397 | .stderr() 398 | .fsmockCommand([ 399 | 'run', 400 | MockFsFactory.DIR_PROJECT, 401 | '--ignore-node-path', 402 | 'has-symbols', 403 | ]) 404 | .it( 405 | 'should generate deploy.zip without default excluded node path', 406 | async ctx => { 407 | expect(ctx.stderr).to.be.empty; 408 | 409 | const outputFilePath = join(MockFsFactory.DIR_PROJECT, 'deploy.zip'); 410 | const mapFiles = await getUnzipedFilesInMap(outputFilePath); 411 | const mapFileKeys = [...mapFiles.keys()]; 412 | 413 | expect( 414 | mapFileKeys.every( 415 | mapFile => !mapFile.startsWith('node_modules/has-symbols'), 416 | ), 417 | ).to.be.true; 418 | }, 419 | ); 420 | }); 421 | 422 | describe('with --output-file flag', () => { 423 | fsTest 424 | .stdout() 425 | .stderr() 426 | .fsmockCommand([ 427 | 'run', 428 | '--output-file', 429 | 'result.zip', 430 | MockFsFactory.DIR_PROJECT, 431 | ]) 432 | .it('should generate result.zip', ctx => { 433 | expect(ctx.stderr).to.be.empty; 434 | 435 | const createdTheDeployZip = existsSync( 436 | join(MockFsFactory.DIR_PROJECT, 'result.zip'), 437 | ); 438 | 439 | expect(createdTheDeployZip).to.be.eq(true); 440 | }); 441 | 442 | fsTest 443 | .stdout() 444 | .stderr() 445 | .fsmockCommand([ 446 | 'run', 447 | '--output-file', 448 | 'result.ts', 449 | MockFsFactory.DIR_PROJECT, 450 | ]) 451 | .catch(err => { 452 | expect(err.message).to.contain('Invalid output file extension'); 453 | }) 454 | .it('should generate error with result.ts'); 455 | }); 456 | 457 | describe('with --output-path flag', () => { 458 | fsTest 459 | .stdout() 460 | .stderr() 461 | .fsmockCommand([ 462 | 'run', 463 | '--output-path', 464 | './result', 465 | MockFsFactory.DIR_PROJECT, 466 | ]) 467 | .it('should generate deploy.zip inside result folder', ctx => { 468 | expect(ctx.stderr).to.be.empty; 469 | 470 | const createdTheDeployZip = existsSync( 471 | join(MockFsFactory.DIR_PROJECT, 'result/deploy.zip'), 472 | ); 473 | 474 | expect(createdTheDeployZip).to.be.eq(true); 475 | }); 476 | }); 477 | 478 | describe('with --quiet flag', () => { 479 | fsTest 480 | .stdout() 481 | .stderr() 482 | .fsmockCommand(['run', MockFsFactory.DIR_PROJECT, '-q']) 483 | .it('should log nothing', ctx => { 484 | expect(ctx.stdout).to.be.empty; 485 | expect(ctx.stderr).to.be.empty; 486 | 487 | const outputFilePath = join(MockFsFactory.DIR_PROJECT, 'deploy.zip'); 488 | const createdTheDeployZip = existsSync(outputFilePath); 489 | 490 | expect(createdTheDeployZip).to.be.eq(true); 491 | }); 492 | }); 493 | 494 | describe('with --minify flag', () => { 495 | fsTest 496 | .stdout() 497 | .stderr() 498 | .fsmockCommand(['run', MockFsFactory.DIR_PROJECT, '--peer', '--dev']) 499 | .it( 500 | `should have output file size as ${dependenciesWithPeerAndDevSize} bytes without minify`, 501 | ctx => { 502 | expect(ctx.stderr).to.be.empty; 503 | 504 | const outputFilePath = join(MockFsFactory.DIR_PROJECT, 'deploy.zip'); 505 | 506 | const stats = statSync(outputFilePath); 507 | 508 | expect(stats.size).to.be.eq(dependenciesWithPeerAndDevSize); 509 | }, 510 | ); 511 | 512 | fsTest 513 | .stdout() 514 | .stderr() 515 | .fsmockCommand([ 516 | 'run', 517 | MockFsFactory.DIR_PROJECT, 518 | '--minify', 519 | '--peer', 520 | '--dev', 521 | ]) 522 | .it( 523 | `should have output file size equal to ${minifyDependenciesSize} with minify`, 524 | ctx => { 525 | expect(ctx.stderr).to.be.empty; 526 | 527 | const outputFilePath = join(MockFsFactory.DIR_PROJECT, 'deploy.zip'); 528 | const stats = statSync(outputFilePath); 529 | 530 | expect(stats.size).to.be.eq(minifyDependenciesSize); 531 | }, 532 | ); 533 | 534 | fsTest 535 | .stdout() 536 | .stderr() 537 | .fsmockCommand([ 538 | 'run', 539 | MockFsFactory.DIR_PROJECT, 540 | '--minify', 541 | '--minify-keep-names', 542 | '--peer', 543 | '--dev', 544 | ]) 545 | .it( 546 | `should have output file size equal to ${minifyDependenciesWithKeepNamesSize} with minify and keep names`, 547 | ctx => { 548 | expect(ctx.stderr).to.be.empty; 549 | 550 | const outputFilePath = join(MockFsFactory.DIR_PROJECT, 'deploy.zip'); 551 | const stats = statSync(outputFilePath); 552 | 553 | expect(stats.size).to.be.eq(minifyDependenciesWithKeepNamesSize); 554 | }, 555 | ); 556 | }); 557 | 558 | describe('with invalid lock file', () => { 559 | fsTest 560 | .stdout() 561 | .stderr() 562 | .fsmockCommand(['run', MockFsFactory.DIR_NO_PACKAGE_LOCK_FILE]) 563 | .catch(err => { 564 | expect(err.message).to.contain('Invalid package-lock.json'); 565 | }) 566 | .it('should throw error if could not find package-lock.json'); 567 | }); 568 | 569 | describe('remove file if already exist', () => { 570 | fsTest 571 | .stdout() 572 | .stderr() 573 | .fsmockCommand(['run', MockFsFactory.DIR_WITH_DEPLOY_FILE]) 574 | .it('should remove file before run', ctx => { 575 | expect(ctx.stdout).to.contain('Removing old output file'); 576 | }); 577 | }); 578 | 579 | describe('with invalid node modules', () => { 580 | fsTest 581 | .stdout() 582 | .stderr() 583 | .fsmockCommand(['run', MockFsFactory.DIR_NO_NODE_MODULES]) 584 | .catch(err => { 585 | expect(err.message).to.contain('Invalid Node Modules'); 586 | }) 587 | .it('should throw error if could not find node_modules'); 588 | }); 589 | 590 | describe('with headless', () => { 591 | fsTest 592 | // @ts-ignore 593 | .stub(Run, 'run', (args: any) => args) 594 | .it('should generate correctly the default', async () => { 595 | const result = await Run.headless({ 596 | dir: './', 597 | }); 598 | 599 | expect(result).to.be.eql(['./']); 600 | }); 601 | 602 | fsTest 603 | // @ts-ignore 604 | .stub(Run, 'run', (args: any) => args) 605 | .it('should generate correctly all flags', async () => { 606 | const result = await Run.headless({ 607 | dir: './', 608 | include: ['dist'], 609 | includeNodePath: ['typeorm/browser'], 610 | ignoreNodePath: ['aws-sdk'], 611 | ignoreFileExt: ['.json'], 612 | disableDefaultIgnoreFileExt: true, 613 | prod: false, 614 | dev: true, 615 | peer: false, 616 | optional: false, 617 | minify: true, 618 | minifyKeepNames: true, 619 | outputPath: './test', 620 | outputFile: 'result.zip', 621 | }); 622 | 623 | expect(result).to.be.eql([ 624 | './', 625 | '-i', 626 | 'dist', 627 | '-e', 628 | '.json', 629 | '--disable-default-ignore-file-ext', 630 | '--include-node-path', 631 | 'typeorm/browser', 632 | '--ignore-node-path', 633 | 'aws-sdk', 634 | '--no-prod', 635 | '--dev', 636 | '--no-peer', 637 | '--no-optional', 638 | '--output-path', 639 | './test', 640 | '--output-file', 641 | 'result.zip', 642 | '--minify', 643 | '--minify-keep-names', 644 | ]); 645 | }); 646 | }); 647 | 648 | describe('test paths in win32', () => { 649 | it('should handle correctly when path is win32 using fix path', () => { 650 | const runCommand = new Run([], {} as any); 651 | const fixPath = runCommand['fixPath']; 652 | 653 | expect( 654 | fixPath('node_modules\\@h4ad\\serverless-adapter\\lib\\index.js'), 655 | ).to.be.eq('node_modules/@h4ad/serverless-adapter/lib/index.js'); 656 | }); 657 | }); 658 | }); 659 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 🚀 Node Modules Packer 3 |

4 | 5 |

6 | Use Cases   |    7 | Usage   |    8 | Examples   |    9 | Headless   |    10 | Benchmarks   |    11 | Reference 12 |

13 | 14 | [![oclif](https://img.shields.io/badge/cli-oclif-brightgreen.svg)](https://oclif.io) 15 | [![npm package][npm-img]][npm-url] 16 | [![Build Status][build-img]][build-url] 17 | [![Downloads][downloads-img]][downloads-url] 18 | [![Issues][issues-img]][issues-url] 19 | [![Code Coverage][codecov-img]][codecov-url] 20 | [![Commitizen Friendly][commitizen-img]][commitizen-url] 21 | [![Semantic Release][semantic-release-img]][semantic-release-url] 22 | 23 | This is a library to package all your node_modules and other files you want inside your project to a zip file. 24 | It's like using `npm prune --production` but without all the heavy I/O operations. 25 | 26 | You can use this library to deploy applications in serverless environments, for example, without having to do 27 | lots of crazy configurations with webpack, also this library allows you to minify all `.js` files using `esbuild`. 28 | 29 | I personally created this library inspired by an internal library I created for my company to deploy our NestJS apps 30 | for AWS Lambda. 31 | With this guy I improve deployment time by up to [441% (284% minified)](./benchmark#results) and reduce the bundle 32 | size by up to [~40% (~55% minified)](./benchmark#results) with the benefit that my Webstorm doesn't go crazy with dependency indexing 33 | every time I deploy because I no longer need to run `npm prune --production` just to get a descending build size. 34 | 35 | > Wait, you're asking me why I build and deploy the APIs on my computer instead of using CI/CD? 36 | > 37 | > Well the answer is pretty simple, I want to be in control of these things ~~and we didn't have a CI/CD until a few months ago.~~ 38 | 39 | # Use cases 40 | 41 | In which cases this library might suit you: 42 | 43 | - If you deploy your nodejs apps in zip file with node_modules. 44 | - If you want to keep the directory structure (eg typeorm). 45 | - If you don't like dealing with webpack and just want to get things done. 46 | - If you use terraform to deploy in serverless environments, just point the output file to terraform and that's it. 47 | - If you want to minify all `.js` files, see `--minify` flag. 48 | - If you want to remap the files, like renaming `dist` to `build`. 49 | - If you like to deploy your app manually. 50 | - This library can give you more control over how you compress your files without having to write a lot of code to do so. 51 | 52 | In which cases this library might not fit you: 53 | 54 | - If you already have some webpack configuration to package your app. 55 | - If you don't need to maintain the directory structure. 56 | - If your nodejs app is very simple (see [serverless-bundle](https://github.com/AnomalyInnovations/serverless-bundle)). 57 | - If you use [serverless.com](https://www.serverless.com/). 58 | - I've personally never used it, but it looks pretty good to use, see their [packaging docs.](https://www.serverless.com/framework/docs/providers/aws/guide/packaging) 59 | 60 | # Usage 61 | 62 | First, install the library with global flag: 63 | 64 | ```bash 65 | npm i -g @h4ad/node-modules-packer 66 | ``` 67 | 68 | Then, enter inside the project you want to pack and run: 69 | 70 | ```bash 71 | node-modules-packer run ./ 72 | ``` 73 | 74 | By default, this command will: 75 | 76 | - Find all production dependencies and will package them all. 77 | - Ignore all development/peer/optional dependencies 78 | - Ignore many common unused file extensions (eg: .md, .d.ts, .js.map, ..etc) 79 | - See the full list [here.](https://github.com/H4ad/node-modules-packer/blob/master/src/common/extensions.ts) 80 | - Output the final zip with the name `deploy.zip`. 81 | 82 | # Examples 83 | 84 | Include more files in the `deploy.zip`: 85 | 86 | ```bash 87 | node-modules-packer run ./ -i dist -i package.json -i package-lock.json 88 | ``` 89 | 90 | During including, if you need to remap, you can use `:` between the paths: 91 | 92 | ```bash 93 | node-modules-packer run ./ -i dist:build -i package.json:dist/package.json 94 | ``` 95 | 96 | Minify all `.js` files to reduce the bundle size: 97 | 98 | ```bash 99 | node-modules-packer run ./ -i dist --minify 100 | ``` 101 | 102 | > All `.js` files are minified, including files and folders that you include with `-i` flag. 103 | 104 | If you want to preserve the class names, properties and other symbols, you can run with `--minify-keep-names`: 105 | 106 | ```bash 107 | node-modules-packer run ./ -i dist --minify --minify-keep-names 108 | ``` 109 | 110 | Exclude unwanted file extensions from node_modules: 111 | 112 | ```bash 113 | # This will exclude all json files from `deploy.zip`. 114 | node-modules-packer run ./ -e .json 115 | ``` 116 | 117 | Include development/peer/optional dependencies (why?): 118 | 119 | ```bash 120 | node-modules-packer run ./ --dev 121 | node-modules-packer run ./ --peer 122 | node-modules-packer run ./ --optional 123 | # or have all at once 124 | node-modules-packer run ./ --dev --peer --optional 125 | ``` 126 | 127 | Disable default ignored extensions (too much or we ignore something you want?): 128 | 129 | See the full list [here.](https://github.com/H4ad/node-modules-packer/blob/v1.0.0/src/common/extensions.ts) 130 | to know what you will leave inside your zip file if you run with this flag. 131 | 132 | ```bash 133 | node-modules-packer run ./ --disable-default-ignore-file-ext 134 | ``` 135 | 136 | Ignore some node folders/paths that you know that should be OUT of your zip file: 137 | 138 | ```bash 139 | node-modules-packer run ./ --ignore-node-path="typeorm/browser" --ignore-node-path="aws-sdk" 140 | ``` 141 | 142 | Or include some node folders/paths that you know that should be INSIDE of your zip file, 143 | this is particulary usefull if you have some dependency with some misconfiguration of their dependencies. 144 | 145 | ```bash 146 | # the path will be concatenated with `node_modules`, so this became 147 | # `node_modules/some-dependency-you-want` 148 | node-modules-packer run ./ --include-node-path="some-dependency-you-want" 149 | ``` 150 | 151 | > We don't include sub-dependencies of these folders, so if that dependency has another 152 | > dependency, that dependency might be outside your zip. 153 | 154 | You can change the output path and the output filename with: 155 | 156 | ```bash 157 | node-modules-packer run ./ --output-path ./deploy --output-file result.zip 158 | ``` 159 | 160 | ## Headless 161 | 162 | You can use this library in headless mode, for example, for cases where there is a lot of customization. 163 | 164 | ```ts 165 | import Run from '@h4ad/node-modules-packer/lib/commands/run'; 166 | 167 | // this is my configuration to deploy my NestJS APIs 168 | // to AWS Lambda 169 | const result = await Run.headless({ 170 | dir: './', 171 | ignoreNodePath: ['typeorm/browser', 'aws-crt/dist/bin', 'aws-crt/dist.browser', 'sqlite3', 'aws-sdk'], 172 | include: ['dist', 'ormconfig.js'], 173 | outputPath: './deploy', 174 | outputFile: 'deploy.zip', 175 | minify: true, 176 | minifyKeepNames: true, 177 | }); 178 | 179 | console.log(result.size); 180 | console.log(result.file); 181 | console.log(result.path); 182 | ``` 183 | 184 | # Benchmarks 185 | 186 | [See here](./benchmark) more about. 187 | 188 | # Reference 189 | 190 |
191 | See commands reference 192 |
193 | 194 | 195 | * [`node-modules-packer autocomplete [SHELL]`](#node-modules-packer-autocomplete-shell) 196 | * [`node-modules-packer commands`](#node-modules-packer-commands) 197 | * [`node-modules-packer help [COMMAND]`](#node-modules-packer-help-command) 198 | * [`node-modules-packer plugins`](#node-modules-packer-plugins) 199 | * [`node-modules-packer plugins:install PLUGIN...`](#node-modules-packer-pluginsinstall-plugin) 200 | * [`node-modules-packer plugins:inspect PLUGIN...`](#node-modules-packer-pluginsinspect-plugin) 201 | * [`node-modules-packer plugins:install PLUGIN...`](#node-modules-packer-pluginsinstall-plugin-1) 202 | * [`node-modules-packer plugins:link PLUGIN`](#node-modules-packer-pluginslink-plugin) 203 | * [`node-modules-packer plugins:uninstall PLUGIN...`](#node-modules-packer-pluginsuninstall-plugin) 204 | * [`node-modules-packer plugins:uninstall PLUGIN...`](#node-modules-packer-pluginsuninstall-plugin-1) 205 | * [`node-modules-packer plugins:uninstall PLUGIN...`](#node-modules-packer-pluginsuninstall-plugin-2) 206 | * [`node-modules-packer plugins:update`](#node-modules-packer-pluginsupdate) 207 | * [`node-modules-packer run [DIR]`](#node-modules-packer-run-dir) 208 | * [`node-modules-packer version`](#node-modules-packer-version) 209 | 210 | ## `node-modules-packer autocomplete [SHELL]` 211 | 212 | display autocomplete installation instructions 213 | 214 | ``` 215 | USAGE 216 | $ node-modules-packer autocomplete [SHELL] [-r] 217 | 218 | ARGUMENTS 219 | SHELL shell type 220 | 221 | FLAGS 222 | -r, --refresh-cache Refresh cache (ignores displaying instructions) 223 | 224 | DESCRIPTION 225 | display autocomplete installation instructions 226 | 227 | EXAMPLES 228 | $ node-modules-packer autocomplete 229 | 230 | $ node-modules-packer autocomplete bash 231 | 232 | $ node-modules-packer autocomplete zsh 233 | 234 | $ node-modules-packer autocomplete --refresh-cache 235 | ``` 236 | 237 | _See code: [@oclif/plugin-autocomplete](https://github.com/oclif/plugin-autocomplete/blob/v1.3.0/src/commands/autocomplete/index.ts)_ 238 | 239 | ## `node-modules-packer commands` 240 | 241 | list all the commands 242 | 243 | ``` 244 | USAGE 245 | $ node-modules-packer commands [--json] [-h] [--hidden] [--tree] [--columns | -x] [--sort ] 246 | [--filter ] [--output csv|json|yaml | | [--csv | --no-truncate]] [--no-header | ] 247 | 248 | FLAGS 249 | -h, --help Show CLI help. 250 | -x, --extended show extra columns 251 | --columns= only show provided columns (comma-separated) 252 | --csv output is csv format [alias: --output=csv] 253 | --filter= filter property by partial string matching, ex: name=foo 254 | --hidden show hidden commands 255 | --no-header hide table header from output 256 | --no-truncate do not truncate output to fit screen 257 | --output=
600 | 601 | [build-img]:https://github.com/H4ad/node-modules-packer/actions/workflows/release.yml/badge.svg 602 | 603 | [build-url]:https://github.com/H4ad/node-modules-packer/actions/workflows/release.yml 604 | 605 | [downloads-img]:https://img.shields.io/npm/dt/@h4ad/node-modules-packer 606 | 607 | [downloads-url]:https://www.npmtrends.com/@h4ad/node-modules-packer 608 | 609 | [npm-img]:https://img.shields.io/npm/v/@h4ad/node-modules-packer 610 | 611 | [npm-url]:https://www.npmjs.com/package/@h4ad/node-modules-packer 612 | 613 | [issues-img]:https://img.shields.io/github/issues/H4ad/node-modules-packer 614 | 615 | [issues-url]:https://github.com/H4ad/node-modules-packer/issues 616 | 617 | [codecov-img]:https://codecov.io/gh/H4ad/node-modules-packer/branch/master/graph/badge.svg 618 | 619 | [codecov-url]:https://codecov.io/gh/H4ad/node-modules-packer 620 | 621 | [semantic-release-img]:https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg 622 | 623 | [semantic-release-url]:https://github.com/semantic-release/semantic-release 624 | 625 | [commitizen-img]:https://img.shields.io/badge/commitizen-friendly-brightgreen.svg 626 | 627 | [commitizen-url]:http://commitizen.github.io/cz-cli/ 628 | -------------------------------------------------------------------------------- /benchmark/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "benchmark", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "benchmark", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@h4ad/node-modules-packer": "file:../", 13 | "archiver": "^5.3.1" 14 | } 15 | }, 16 | "..": { 17 | "version": "1.1.0", 18 | "license": "MIT", 19 | "dependencies": { 20 | "@h4ad/dependency-extractor": "^1.0.0", 21 | "@oclif/core": "^1", 22 | "@oclif/plugin-autocomplete": "^1.3.0", 23 | "@oclif/plugin-commands": "^2.2.0", 24 | "@oclif/plugin-help": "^5", 25 | "@oclif/plugin-plugins": "^2.1.0", 26 | "@oclif/plugin-version": "^1.1.1", 27 | "bluebird": "^3.7.2", 28 | "esbuild": "^0.15.3", 29 | "plist": "^3.0.5", 30 | "rimraf": "^3.0.2", 31 | "semver": "^7.3.7", 32 | "terser": "^5.14.2", 33 | "tslib": "^2.4.0", 34 | "uglify-js": "^3.16.3", 35 | "yazl": "^2.5.1" 36 | }, 37 | "bin": { 38 | "pack": "bin/run" 39 | }, 40 | "devDependencies": { 41 | "@oclif/test": "^2", 42 | "@semantic-release/changelog": "^6.0.1", 43 | "@semantic-release/git": "^10.0.1", 44 | "@semantic-release/github": "^8.0.4", 45 | "@types/bluebird": "^3.5.36", 46 | "@types/chai": "^4", 47 | "@types/mocha": "^9.0.0", 48 | "@types/mock-fs": "^4.13.1", 49 | "@types/node": "12.20.43", 50 | "@types/plist": "^3.0.2", 51 | "@types/rimraf": "^3.0.2", 52 | "@types/terser": "^3.12.0", 53 | "@types/uglify-js": "^3.16.0", 54 | "@types/unzipper": "^0.10.5", 55 | "@types/yazl": "^2.4.2", 56 | "@typescript-eslint/eslint-plugin": "^5.12.1", 57 | "@typescript-eslint/parser": "^5.12.1", 58 | "c8": "^7.12.0", 59 | "chai": "^4", 60 | "codecov": "^3.8.3", 61 | "commitizen": "^4.2.4", 62 | "cz-conventional-changelog": "^3.3.0", 63 | "ejs": "^3.1.6", 64 | "eslint": "^8.9.0", 65 | "eslint-config-oclif": "^4", 66 | "eslint-config-oclif-typescript": "^1.0.2", 67 | "eslint-config-prettier": "^8.4.0", 68 | "eslint-plugin-import": "^2.25.4", 69 | "eslint-plugin-node": "^11.1.0", 70 | "eslint-plugin-prettier": "^4.0.0", 71 | "husky": "^6.0.0", 72 | "lint-staged": "^10.5.4", 73 | "mocha": "^9", 74 | "mock-fs": "^5.1.2", 75 | "nyc": "^15.1.0", 76 | "oclif": "^3", 77 | "prettier": "^2.5.1", 78 | "semantic-release": "^19.0.3", 79 | "tape": "^5.5.3", 80 | "ts-node": "^10.4.0", 81 | "typescript": "^4.5.5", 82 | "unzipper": "^0.10.11" 83 | }, 84 | "engines": { 85 | "node": ">=12.0" 86 | } 87 | }, 88 | "node_modules/@h4ad/node-modules-packer": { 89 | "resolved": "..", 90 | "link": true 91 | }, 92 | "node_modules/archiver": { 93 | "version": "5.3.1", 94 | "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.1.tgz", 95 | "integrity": "sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w==", 96 | "dependencies": { 97 | "archiver-utils": "^2.1.0", 98 | "async": "^3.2.3", 99 | "buffer-crc32": "^0.2.1", 100 | "readable-stream": "^3.6.0", 101 | "readdir-glob": "^1.0.0", 102 | "tar-stream": "^2.2.0", 103 | "zip-stream": "^4.1.0" 104 | }, 105 | "engines": { 106 | "node": ">= 10" 107 | } 108 | }, 109 | "node_modules/archiver-utils": { 110 | "version": "2.1.0", 111 | "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", 112 | "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", 113 | "dependencies": { 114 | "glob": "^7.1.4", 115 | "graceful-fs": "^4.2.0", 116 | "lazystream": "^1.0.0", 117 | "lodash.defaults": "^4.2.0", 118 | "lodash.difference": "^4.5.0", 119 | "lodash.flatten": "^4.4.0", 120 | "lodash.isplainobject": "^4.0.6", 121 | "lodash.union": "^4.6.0", 122 | "normalize-path": "^3.0.0", 123 | "readable-stream": "^2.0.0" 124 | }, 125 | "engines": { 126 | "node": ">= 6" 127 | } 128 | }, 129 | "node_modules/archiver-utils/node_modules/readable-stream": { 130 | "version": "2.3.7", 131 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", 132 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", 133 | "dependencies": { 134 | "core-util-is": "~1.0.0", 135 | "inherits": "~2.0.3", 136 | "isarray": "~1.0.0", 137 | "process-nextick-args": "~2.0.0", 138 | "safe-buffer": "~5.1.1", 139 | "string_decoder": "~1.1.1", 140 | "util-deprecate": "~1.0.1" 141 | } 142 | }, 143 | "node_modules/archiver-utils/node_modules/safe-buffer": { 144 | "version": "5.1.2", 145 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 146 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 147 | }, 148 | "node_modules/async": { 149 | "version": "3.2.4", 150 | "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", 151 | "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" 152 | }, 153 | "node_modules/balanced-match": { 154 | "version": "1.0.2", 155 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 156 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 157 | }, 158 | "node_modules/base64-js": { 159 | "version": "1.5.1", 160 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 161 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 162 | "funding": [ 163 | { 164 | "type": "github", 165 | "url": "https://github.com/sponsors/feross" 166 | }, 167 | { 168 | "type": "patreon", 169 | "url": "https://www.patreon.com/feross" 170 | }, 171 | { 172 | "type": "consulting", 173 | "url": "https://feross.org/support" 174 | } 175 | ] 176 | }, 177 | "node_modules/bl": { 178 | "version": "4.1.0", 179 | "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", 180 | "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", 181 | "dependencies": { 182 | "buffer": "^5.5.0", 183 | "inherits": "^2.0.4", 184 | "readable-stream": "^3.4.0" 185 | } 186 | }, 187 | "node_modules/brace-expansion": { 188 | "version": "1.1.11", 189 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 190 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 191 | "dependencies": { 192 | "balanced-match": "^1.0.0", 193 | "concat-map": "0.0.1" 194 | } 195 | }, 196 | "node_modules/buffer": { 197 | "version": "5.7.1", 198 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 199 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 200 | "funding": [ 201 | { 202 | "type": "github", 203 | "url": "https://github.com/sponsors/feross" 204 | }, 205 | { 206 | "type": "patreon", 207 | "url": "https://www.patreon.com/feross" 208 | }, 209 | { 210 | "type": "consulting", 211 | "url": "https://feross.org/support" 212 | } 213 | ], 214 | "dependencies": { 215 | "base64-js": "^1.3.1", 216 | "ieee754": "^1.1.13" 217 | } 218 | }, 219 | "node_modules/buffer-crc32": { 220 | "version": "0.2.13", 221 | "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", 222 | "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", 223 | "engines": { 224 | "node": "*" 225 | } 226 | }, 227 | "node_modules/compress-commons": { 228 | "version": "4.1.1", 229 | "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.1.tgz", 230 | "integrity": "sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==", 231 | "dependencies": { 232 | "buffer-crc32": "^0.2.13", 233 | "crc32-stream": "^4.0.2", 234 | "normalize-path": "^3.0.0", 235 | "readable-stream": "^3.6.0" 236 | }, 237 | "engines": { 238 | "node": ">= 10" 239 | } 240 | }, 241 | "node_modules/concat-map": { 242 | "version": "0.0.1", 243 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 244 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" 245 | }, 246 | "node_modules/core-util-is": { 247 | "version": "1.0.3", 248 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", 249 | "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" 250 | }, 251 | "node_modules/crc-32": { 252 | "version": "1.2.2", 253 | "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", 254 | "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", 255 | "bin": { 256 | "crc32": "bin/crc32.njs" 257 | }, 258 | "engines": { 259 | "node": ">=0.8" 260 | } 261 | }, 262 | "node_modules/crc32-stream": { 263 | "version": "4.0.2", 264 | "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.2.tgz", 265 | "integrity": "sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==", 266 | "dependencies": { 267 | "crc-32": "^1.2.0", 268 | "readable-stream": "^3.4.0" 269 | }, 270 | "engines": { 271 | "node": ">= 10" 272 | } 273 | }, 274 | "node_modules/end-of-stream": { 275 | "version": "1.4.4", 276 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 277 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 278 | "dependencies": { 279 | "once": "^1.4.0" 280 | } 281 | }, 282 | "node_modules/fs-constants": { 283 | "version": "1.0.0", 284 | "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", 285 | "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" 286 | }, 287 | "node_modules/fs.realpath": { 288 | "version": "1.0.0", 289 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 290 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" 291 | }, 292 | "node_modules/glob": { 293 | "version": "7.2.3", 294 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 295 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 296 | "dependencies": { 297 | "fs.realpath": "^1.0.0", 298 | "inflight": "^1.0.4", 299 | "inherits": "2", 300 | "minimatch": "^3.1.1", 301 | "once": "^1.3.0", 302 | "path-is-absolute": "^1.0.0" 303 | }, 304 | "engines": { 305 | "node": "*" 306 | }, 307 | "funding": { 308 | "url": "https://github.com/sponsors/isaacs" 309 | } 310 | }, 311 | "node_modules/graceful-fs": { 312 | "version": "4.2.10", 313 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", 314 | "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" 315 | }, 316 | "node_modules/ieee754": { 317 | "version": "1.2.1", 318 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 319 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 320 | "funding": [ 321 | { 322 | "type": "github", 323 | "url": "https://github.com/sponsors/feross" 324 | }, 325 | { 326 | "type": "patreon", 327 | "url": "https://www.patreon.com/feross" 328 | }, 329 | { 330 | "type": "consulting", 331 | "url": "https://feross.org/support" 332 | } 333 | ] 334 | }, 335 | "node_modules/inflight": { 336 | "version": "1.0.6", 337 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 338 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 339 | "dependencies": { 340 | "once": "^1.3.0", 341 | "wrappy": "1" 342 | } 343 | }, 344 | "node_modules/inherits": { 345 | "version": "2.0.4", 346 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 347 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 348 | }, 349 | "node_modules/isarray": { 350 | "version": "1.0.0", 351 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 352 | "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" 353 | }, 354 | "node_modules/lazystream": { 355 | "version": "1.0.1", 356 | "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", 357 | "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", 358 | "dependencies": { 359 | "readable-stream": "^2.0.5" 360 | }, 361 | "engines": { 362 | "node": ">= 0.6.3" 363 | } 364 | }, 365 | "node_modules/lazystream/node_modules/readable-stream": { 366 | "version": "2.3.7", 367 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", 368 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", 369 | "dependencies": { 370 | "core-util-is": "~1.0.0", 371 | "inherits": "~2.0.3", 372 | "isarray": "~1.0.0", 373 | "process-nextick-args": "~2.0.0", 374 | "safe-buffer": "~5.1.1", 375 | "string_decoder": "~1.1.1", 376 | "util-deprecate": "~1.0.1" 377 | } 378 | }, 379 | "node_modules/lazystream/node_modules/safe-buffer": { 380 | "version": "5.1.2", 381 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 382 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 383 | }, 384 | "node_modules/lodash.defaults": { 385 | "version": "4.2.0", 386 | "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", 387 | "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" 388 | }, 389 | "node_modules/lodash.difference": { 390 | "version": "4.5.0", 391 | "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", 392 | "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==" 393 | }, 394 | "node_modules/lodash.flatten": { 395 | "version": "4.4.0", 396 | "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", 397 | "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" 398 | }, 399 | "node_modules/lodash.isplainobject": { 400 | "version": "4.0.6", 401 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", 402 | "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" 403 | }, 404 | "node_modules/lodash.union": { 405 | "version": "4.6.0", 406 | "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", 407 | "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==" 408 | }, 409 | "node_modules/minimatch": { 410 | "version": "3.1.2", 411 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 412 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 413 | "dependencies": { 414 | "brace-expansion": "^1.1.7" 415 | }, 416 | "engines": { 417 | "node": "*" 418 | } 419 | }, 420 | "node_modules/normalize-path": { 421 | "version": "3.0.0", 422 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 423 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 424 | "engines": { 425 | "node": ">=0.10.0" 426 | } 427 | }, 428 | "node_modules/once": { 429 | "version": "1.4.0", 430 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 431 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 432 | "dependencies": { 433 | "wrappy": "1" 434 | } 435 | }, 436 | "node_modules/path-is-absolute": { 437 | "version": "1.0.1", 438 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 439 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 440 | "engines": { 441 | "node": ">=0.10.0" 442 | } 443 | }, 444 | "node_modules/process-nextick-args": { 445 | "version": "2.0.1", 446 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 447 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" 448 | }, 449 | "node_modules/readable-stream": { 450 | "version": "3.6.0", 451 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 452 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 453 | "dependencies": { 454 | "inherits": "^2.0.3", 455 | "string_decoder": "^1.1.1", 456 | "util-deprecate": "^1.0.1" 457 | }, 458 | "engines": { 459 | "node": ">= 6" 460 | } 461 | }, 462 | "node_modules/readdir-glob": { 463 | "version": "1.1.2", 464 | "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.2.tgz", 465 | "integrity": "sha512-6RLVvwJtVwEDfPdn6X6Ille4/lxGl0ATOY4FN/B9nxQcgOazvvI0nodiD19ScKq0PvA/29VpaOQML36o5IzZWA==", 466 | "dependencies": { 467 | "minimatch": "^5.1.0" 468 | } 469 | }, 470 | "node_modules/readdir-glob/node_modules/brace-expansion": { 471 | "version": "2.0.1", 472 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 473 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 474 | "dependencies": { 475 | "balanced-match": "^1.0.0" 476 | } 477 | }, 478 | "node_modules/readdir-glob/node_modules/minimatch": { 479 | "version": "5.1.0", 480 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", 481 | "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", 482 | "dependencies": { 483 | "brace-expansion": "^2.0.1" 484 | }, 485 | "engines": { 486 | "node": ">=10" 487 | } 488 | }, 489 | "node_modules/string_decoder": { 490 | "version": "1.1.1", 491 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 492 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 493 | "dependencies": { 494 | "safe-buffer": "~5.1.0" 495 | } 496 | }, 497 | "node_modules/string_decoder/node_modules/safe-buffer": { 498 | "version": "5.1.2", 499 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 500 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 501 | }, 502 | "node_modules/tar-stream": { 503 | "version": "2.2.0", 504 | "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", 505 | "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", 506 | "dependencies": { 507 | "bl": "^4.0.3", 508 | "end-of-stream": "^1.4.1", 509 | "fs-constants": "^1.0.0", 510 | "inherits": "^2.0.3", 511 | "readable-stream": "^3.1.1" 512 | }, 513 | "engines": { 514 | "node": ">=6" 515 | } 516 | }, 517 | "node_modules/util-deprecate": { 518 | "version": "1.0.2", 519 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 520 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" 521 | }, 522 | "node_modules/wrappy": { 523 | "version": "1.0.2", 524 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 525 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" 526 | }, 527 | "node_modules/zip-stream": { 528 | "version": "4.1.0", 529 | "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.0.tgz", 530 | "integrity": "sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==", 531 | "dependencies": { 532 | "archiver-utils": "^2.1.0", 533 | "compress-commons": "^4.1.0", 534 | "readable-stream": "^3.6.0" 535 | }, 536 | "engines": { 537 | "node": ">= 10" 538 | } 539 | } 540 | }, 541 | "dependencies": { 542 | "@h4ad/node-modules-packer": { 543 | "version": "file:..", 544 | "requires": { 545 | "@h4ad/dependency-extractor": "^1.0.0", 546 | "@oclif/core": "^1", 547 | "@oclif/plugin-autocomplete": "^1.3.0", 548 | "@oclif/plugin-commands": "^2.2.0", 549 | "@oclif/plugin-help": "^5", 550 | "@oclif/plugin-plugins": "^2.1.0", 551 | "@oclif/plugin-version": "^1.1.1", 552 | "@oclif/test": "^2", 553 | "@semantic-release/changelog": "^6.0.1", 554 | "@semantic-release/git": "^10.0.1", 555 | "@semantic-release/github": "^8.0.4", 556 | "@types/bluebird": "^3.5.36", 557 | "@types/chai": "^4", 558 | "@types/mocha": "^9.0.0", 559 | "@types/mock-fs": "^4.13.1", 560 | "@types/node": "12.20.43", 561 | "@types/plist": "^3.0.2", 562 | "@types/rimraf": "^3.0.2", 563 | "@types/terser": "^3.12.0", 564 | "@types/uglify-js": "^3.16.0", 565 | "@types/unzipper": "^0.10.5", 566 | "@types/yazl": "^2.4.2", 567 | "@typescript-eslint/eslint-plugin": "^5.12.1", 568 | "@typescript-eslint/parser": "^5.12.1", 569 | "bluebird": "^3.7.2", 570 | "c8": "^7.12.0", 571 | "chai": "^4", 572 | "codecov": "^3.8.3", 573 | "commitizen": "^4.2.4", 574 | "cz-conventional-changelog": "^3.3.0", 575 | "ejs": "^3.1.6", 576 | "esbuild": "^0.15.3", 577 | "eslint": "^8.9.0", 578 | "eslint-config-oclif": "^4", 579 | "eslint-config-oclif-typescript": "^1.0.2", 580 | "eslint-config-prettier": "^8.4.0", 581 | "eslint-plugin-import": "^2.25.4", 582 | "eslint-plugin-node": "^11.1.0", 583 | "eslint-plugin-prettier": "^4.0.0", 584 | "husky": "^6.0.0", 585 | "lint-staged": "^10.5.4", 586 | "mocha": "^9", 587 | "mock-fs": "^5.1.2", 588 | "nyc": "^15.1.0", 589 | "oclif": "^3", 590 | "plist": "^3.0.5", 591 | "prettier": "^2.5.1", 592 | "rimraf": "^3.0.2", 593 | "semantic-release": "^19.0.3", 594 | "semver": "^7.3.7", 595 | "tape": "^5.5.3", 596 | "terser": "^5.14.2", 597 | "ts-node": "^10.4.0", 598 | "tslib": "^2.4.0", 599 | "typescript": "^4.5.5", 600 | "uglify-js": "^3.16.3", 601 | "unzipper": "^0.10.11", 602 | "yazl": "^2.5.1" 603 | } 604 | }, 605 | "archiver": { 606 | "version": "5.3.1", 607 | "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.1.tgz", 608 | "integrity": "sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w==", 609 | "requires": { 610 | "archiver-utils": "^2.1.0", 611 | "async": "^3.2.3", 612 | "buffer-crc32": "^0.2.1", 613 | "readable-stream": "^3.6.0", 614 | "readdir-glob": "^1.0.0", 615 | "tar-stream": "^2.2.0", 616 | "zip-stream": "^4.1.0" 617 | } 618 | }, 619 | "archiver-utils": { 620 | "version": "2.1.0", 621 | "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", 622 | "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", 623 | "requires": { 624 | "glob": "^7.1.4", 625 | "graceful-fs": "^4.2.0", 626 | "lazystream": "^1.0.0", 627 | "lodash.defaults": "^4.2.0", 628 | "lodash.difference": "^4.5.0", 629 | "lodash.flatten": "^4.4.0", 630 | "lodash.isplainobject": "^4.0.6", 631 | "lodash.union": "^4.6.0", 632 | "normalize-path": "^3.0.0", 633 | "readable-stream": "^2.0.0" 634 | }, 635 | "dependencies": { 636 | "readable-stream": { 637 | "version": "2.3.7", 638 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", 639 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", 640 | "requires": { 641 | "core-util-is": "~1.0.0", 642 | "inherits": "~2.0.3", 643 | "isarray": "~1.0.0", 644 | "process-nextick-args": "~2.0.0", 645 | "safe-buffer": "~5.1.1", 646 | "string_decoder": "~1.1.1", 647 | "util-deprecate": "~1.0.1" 648 | } 649 | }, 650 | "safe-buffer": { 651 | "version": "5.1.2", 652 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 653 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 654 | } 655 | } 656 | }, 657 | "async": { 658 | "version": "3.2.4", 659 | "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", 660 | "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" 661 | }, 662 | "balanced-match": { 663 | "version": "1.0.2", 664 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 665 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 666 | }, 667 | "base64-js": { 668 | "version": "1.5.1", 669 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 670 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" 671 | }, 672 | "bl": { 673 | "version": "4.1.0", 674 | "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", 675 | "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", 676 | "requires": { 677 | "buffer": "^5.5.0", 678 | "inherits": "^2.0.4", 679 | "readable-stream": "^3.4.0" 680 | } 681 | }, 682 | "brace-expansion": { 683 | "version": "1.1.11", 684 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 685 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 686 | "requires": { 687 | "balanced-match": "^1.0.0", 688 | "concat-map": "0.0.1" 689 | } 690 | }, 691 | "buffer": { 692 | "version": "5.7.1", 693 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 694 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 695 | "requires": { 696 | "base64-js": "^1.3.1", 697 | "ieee754": "^1.1.13" 698 | } 699 | }, 700 | "buffer-crc32": { 701 | "version": "0.2.13", 702 | "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", 703 | "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==" 704 | }, 705 | "compress-commons": { 706 | "version": "4.1.1", 707 | "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.1.tgz", 708 | "integrity": "sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==", 709 | "requires": { 710 | "buffer-crc32": "^0.2.13", 711 | "crc32-stream": "^4.0.2", 712 | "normalize-path": "^3.0.0", 713 | "readable-stream": "^3.6.0" 714 | } 715 | }, 716 | "concat-map": { 717 | "version": "0.0.1", 718 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 719 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" 720 | }, 721 | "core-util-is": { 722 | "version": "1.0.3", 723 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", 724 | "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" 725 | }, 726 | "crc-32": { 727 | "version": "1.2.2", 728 | "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", 729 | "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==" 730 | }, 731 | "crc32-stream": { 732 | "version": "4.0.2", 733 | "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.2.tgz", 734 | "integrity": "sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==", 735 | "requires": { 736 | "crc-32": "^1.2.0", 737 | "readable-stream": "^3.4.0" 738 | } 739 | }, 740 | "end-of-stream": { 741 | "version": "1.4.4", 742 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 743 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 744 | "requires": { 745 | "once": "^1.4.0" 746 | } 747 | }, 748 | "fs-constants": { 749 | "version": "1.0.0", 750 | "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", 751 | "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" 752 | }, 753 | "fs.realpath": { 754 | "version": "1.0.0", 755 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 756 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" 757 | }, 758 | "glob": { 759 | "version": "7.2.3", 760 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 761 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 762 | "requires": { 763 | "fs.realpath": "^1.0.0", 764 | "inflight": "^1.0.4", 765 | "inherits": "2", 766 | "minimatch": "^3.1.1", 767 | "once": "^1.3.0", 768 | "path-is-absolute": "^1.0.0" 769 | } 770 | }, 771 | "graceful-fs": { 772 | "version": "4.2.10", 773 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", 774 | "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" 775 | }, 776 | "ieee754": { 777 | "version": "1.2.1", 778 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 779 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" 780 | }, 781 | "inflight": { 782 | "version": "1.0.6", 783 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 784 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 785 | "requires": { 786 | "once": "^1.3.0", 787 | "wrappy": "1" 788 | } 789 | }, 790 | "inherits": { 791 | "version": "2.0.4", 792 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 793 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 794 | }, 795 | "isarray": { 796 | "version": "1.0.0", 797 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 798 | "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" 799 | }, 800 | "lazystream": { 801 | "version": "1.0.1", 802 | "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", 803 | "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", 804 | "requires": { 805 | "readable-stream": "^2.0.5" 806 | }, 807 | "dependencies": { 808 | "readable-stream": { 809 | "version": "2.3.7", 810 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", 811 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", 812 | "requires": { 813 | "core-util-is": "~1.0.0", 814 | "inherits": "~2.0.3", 815 | "isarray": "~1.0.0", 816 | "process-nextick-args": "~2.0.0", 817 | "safe-buffer": "~5.1.1", 818 | "string_decoder": "~1.1.1", 819 | "util-deprecate": "~1.0.1" 820 | } 821 | }, 822 | "safe-buffer": { 823 | "version": "5.1.2", 824 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 825 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 826 | } 827 | } 828 | }, 829 | "lodash.defaults": { 830 | "version": "4.2.0", 831 | "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", 832 | "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" 833 | }, 834 | "lodash.difference": { 835 | "version": "4.5.0", 836 | "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", 837 | "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==" 838 | }, 839 | "lodash.flatten": { 840 | "version": "4.4.0", 841 | "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", 842 | "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" 843 | }, 844 | "lodash.isplainobject": { 845 | "version": "4.0.6", 846 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", 847 | "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" 848 | }, 849 | "lodash.union": { 850 | "version": "4.6.0", 851 | "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", 852 | "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==" 853 | }, 854 | "minimatch": { 855 | "version": "3.1.2", 856 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 857 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 858 | "requires": { 859 | "brace-expansion": "^1.1.7" 860 | } 861 | }, 862 | "normalize-path": { 863 | "version": "3.0.0", 864 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 865 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" 866 | }, 867 | "once": { 868 | "version": "1.4.0", 869 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 870 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 871 | "requires": { 872 | "wrappy": "1" 873 | } 874 | }, 875 | "path-is-absolute": { 876 | "version": "1.0.1", 877 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 878 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" 879 | }, 880 | "process-nextick-args": { 881 | "version": "2.0.1", 882 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 883 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" 884 | }, 885 | "readable-stream": { 886 | "version": "3.6.0", 887 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 888 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 889 | "requires": { 890 | "inherits": "^2.0.3", 891 | "string_decoder": "^1.1.1", 892 | "util-deprecate": "^1.0.1" 893 | } 894 | }, 895 | "readdir-glob": { 896 | "version": "1.1.2", 897 | "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.2.tgz", 898 | "integrity": "sha512-6RLVvwJtVwEDfPdn6X6Ille4/lxGl0ATOY4FN/B9nxQcgOazvvI0nodiD19ScKq0PvA/29VpaOQML36o5IzZWA==", 899 | "requires": { 900 | "minimatch": "^5.1.0" 901 | }, 902 | "dependencies": { 903 | "brace-expansion": { 904 | "version": "2.0.1", 905 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 906 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 907 | "requires": { 908 | "balanced-match": "^1.0.0" 909 | } 910 | }, 911 | "minimatch": { 912 | "version": "5.1.0", 913 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", 914 | "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", 915 | "requires": { 916 | "brace-expansion": "^2.0.1" 917 | } 918 | } 919 | } 920 | }, 921 | "string_decoder": { 922 | "version": "1.1.1", 923 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 924 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 925 | "requires": { 926 | "safe-buffer": "~5.1.0" 927 | }, 928 | "dependencies": { 929 | "safe-buffer": { 930 | "version": "5.1.2", 931 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 932 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 933 | } 934 | } 935 | }, 936 | "tar-stream": { 937 | "version": "2.2.0", 938 | "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", 939 | "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", 940 | "requires": { 941 | "bl": "^4.0.3", 942 | "end-of-stream": "^1.4.1", 943 | "fs-constants": "^1.0.0", 944 | "inherits": "^2.0.3", 945 | "readable-stream": "^3.1.1" 946 | } 947 | }, 948 | "util-deprecate": { 949 | "version": "1.0.2", 950 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 951 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" 952 | }, 953 | "wrappy": { 954 | "version": "1.0.2", 955 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 956 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" 957 | }, 958 | "zip-stream": { 959 | "version": "4.1.0", 960 | "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.0.tgz", 961 | "integrity": "sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==", 962 | "requires": { 963 | "archiver-utils": "^2.1.0", 964 | "compress-commons": "^4.1.0", 965 | "readable-stream": "^3.6.0" 966 | } 967 | } 968 | } 969 | } 970 | --------------------------------------------------------------------------------