├── test ├── fixtures │ ├── modA.ts │ ├── modC.ts │ └── modB.ts └── test-shim.ts ├── .eslintignore ├── .eslintrc.json ├── .prettierignore ├── .github ├── release-please.yml └── workflows │ ├── release.yaml │ ├── codeql-analysis.yml │ └── ci.yaml ├── doc └── trace.png ├── .gitignore ├── renovate.json ├── tsconfig.json ├── src ├── index.ts ├── shim.ts ├── perf-trace.ts └── cli.ts ├── .prettierrc.js ├── package.json ├── README.md ├── CHANGELOG.md └── LICENSE /test/fixtures/modA.ts: -------------------------------------------------------------------------------- 1 | export const name = 'modA'; 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | build/ 3 | test/fixtures 4 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./node_modules/gts" 3 | } 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | build/ 3 | **/node_modules 4 | test/fixtures 5 | -------------------------------------------------------------------------------- /.github/release-please.yml: -------------------------------------------------------------------------------- 1 | releaseType: node 2 | handleGHRelease: true 3 | primaryBranch: main 4 | -------------------------------------------------------------------------------- /doc/trace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/require-so-slow/HEAD/doc/trace.png -------------------------------------------------------------------------------- /test/fixtures/modC.ts: -------------------------------------------------------------------------------- 1 | import * as A from './modA'; 2 | import * as B from './modB'; 3 | 4 | export const name = 'modC'; 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .nyc_output 3 | .vscode 4 | build 5 | coverage 6 | node_modules 7 | npm-debug.log 8 | yarn.lock 9 | *.trace 10 | -------------------------------------------------------------------------------- /test/fixtures/modB.ts: -------------------------------------------------------------------------------- 1 | import {now} from '../../src/perf-trace'; 2 | 3 | // Take a long time. 4 | const DELAY = 2000; 5 | const endMicroseconds = now() + DELAY; 6 | while (now() <= endMicroseconds) { 7 | /* spin */ 8 | } 9 | 10 | export const name = 'modB'; 11 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base", 4 | "docker:disable" 5 | ], 6 | "pinVersions": false, 7 | "rebaseStalePrs": true, 8 | "gitAuthor": null, 9 | "packageRules": [ 10 | { 11 | "extends": "packages:linters", 12 | "groupName": "linters" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./node_modules/gts/tsconfig-google.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "build" 6 | }, 7 | "include": [ 8 | "src/*.ts", 9 | "src/**/*.ts", 10 | "test/*.ts", 11 | "test/**/*.ts" 12 | ], 13 | "exclude": [ 14 | "node_modules" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import {resolve} from 'path'; 2 | import * as shim from './shim'; 3 | 4 | export function write(path: string) { 5 | shim.write(path); 6 | } 7 | 8 | shim.createShim(); 9 | 10 | if (module.parent && module.parent.id === 'internal/preload') { 11 | const tracefile = resolve( 12 | process.env['TRACE_OUTFILE'] || 'require-so-slow.trace' 13 | ); 14 | process.on('exit', () => { 15 | write(tracefile); 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | release: 3 | types: [published] 4 | name: release 5 | jobs: 6 | release-please: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - uses: actions/setup-node@v2 11 | with: 12 | node-version: 14 13 | registry-url: 'https://wombat-dressing-room.appspot.com' 14 | - run: npm ci 15 | - run: npm publish 16 | env: 17 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 18 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: CodeQL 2 | on: 3 | push: 4 | branches: [ main ] 5 | pull_request: 6 | # The branches below must be a subset of the branches above 7 | branches: [ main ] 8 | schedule: 9 | - cron: '38 12 * * 6' 10 | jobs: 11 | analyze: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: github/codeql-action/init@v1 16 | with: 17 | languages: 'javascript' 18 | - uses: github/codeql-action/analyze@v1 19 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | module.exports = { 16 | ...require('gts/.prettierrc.json') 17 | } 18 | -------------------------------------------------------------------------------- /src/shim.ts: -------------------------------------------------------------------------------- 1 | import * as perfTrace from './perf-trace'; 2 | // eslint-disable-next-line @typescript-eslint/no-var-requires 3 | const MODULE = require('module'); 4 | 5 | const REQUIRE_SO_SLOW = Symbol('require-so-slow-monkey-patch'); 6 | 7 | export function createShim() { 8 | const orig = MODULE._load; 9 | if (orig[REQUIRE_SO_SLOW]) { 10 | return; 11 | } 12 | MODULE._load = function (request: string) { 13 | // eslint-disable-next-line prefer-rest-params 14 | const args = arguments; 15 | let exports; 16 | perfTrace.wrap(`require ${request}`, () => { 17 | exports = orig.apply(this, args); 18 | }); 19 | return exports; 20 | }; 21 | MODULE._load[REQUIRE_SO_SLOW] = true; 22 | } 23 | 24 | export function write(path: string) { 25 | perfTrace.write(path); 26 | } 27 | 28 | export function getAndClearEvents(): perfTrace.Event[] { 29 | return perfTrace.getAndClearEvents(); 30 | } 31 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [ $default-branch ] 4 | pull_request: 5 | name: ci 6 | jobs: 7 | test: 8 | runs-on: ubuntu-latest 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | node: [10, 12, 14, 15] 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions/setup-node@v2 16 | with: 17 | node-version: ${{ matrix.node }} 18 | - run: node --version 19 | - run: npm ci 20 | - run: npm test 21 | - uses: codecov/codecov-action@v1 22 | if: matrix.node == 14 23 | windows: 24 | runs-on: windows-latest 25 | steps: 26 | - uses: actions/checkout@v2 27 | - uses: actions/setup-node@v2 28 | with: 29 | node-version: 14 30 | - run: npm ci 31 | - run: npm test 32 | lint: 33 | runs-on: ubuntu-latest 34 | steps: 35 | - uses: actions/checkout@v2 36 | - uses: actions/setup-node@v2 37 | with: 38 | node-version: 14 39 | - run: npm ci 40 | - run: npm run lint 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "require-so-slow", 3 | "version": "2.0.5", 4 | "description": "`require`s taking too much time? Profile 'em.", 5 | "repository": "ofrobots/require-so-slow", 6 | "main": "build/src/index.js", 7 | "types": "build/src/index.d.ts", 8 | "bin": { 9 | "rss": "build/src/cli.js", 10 | "require-so-slow": "build/src/cli.js" 11 | }, 12 | "dependencies": { 13 | "meow": "^9.0.0", 14 | "node-fetch": "^2.2.0", 15 | "npm-package-arg": "^8.0.0", 16 | "tmp": "0.2.1", 17 | "update-notifier": "^5.0.0" 18 | }, 19 | "devDependencies": { 20 | "@types/meow": "^5.0.0", 21 | "@types/node": "^14.0.0", 22 | "@types/node-fetch": "^2.1.2", 23 | "@types/npm-package-arg": "^6.0.0", 24 | "@types/tape": "^4.2.32", 25 | "@types/tmp": "0.2.1", 26 | "@types/update-notifier": "^5.0.0", 27 | "c8": "^7.1.0", 28 | "codecov": "^3.0.2", 29 | "gts": "^3.0.0", 30 | "source-map-support": "^0.5.6", 31 | "tape": "^5.0.0", 32 | "typescript": "~4.4.0" 33 | }, 34 | "engines": { 35 | "node": ">=10" 36 | }, 37 | "scripts": { 38 | "test": "c8 tape -r source-map-support/register build/test/test*.js", 39 | "lint": "gts check", 40 | "clean": "gts clean", 41 | "codecov": "c8 report --reporter=json && codecov -f coverage/*.json", 42 | "compile": "tsc -p .", 43 | "fix": "gts fix", 44 | "prepare": "npm run compile", 45 | "pretest": "npm run compile" 46 | }, 47 | "files": [ 48 | "build/src" 49 | ], 50 | "keywords": [ 51 | "require", 52 | "profiler", 53 | "timeline", 54 | "trace_events", 55 | "trace" 56 | ], 57 | "author": "", 58 | "license": "Apache-2.0" 59 | } 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # require-so-slow 2 | # This repository is no longer maintained. 3 | 4 | * Wondering why your applications is slow to start? 5 | * `require` seems to take an eternity? 6 | * Wonder no more! 7 | 8 | This module produces a timeline of your `require`s and produces an output 9 | that you can load in the Chrome [Timeline Viewer][]. 10 | 11 | [![Trace Viewer](doc/trace.png)](https://chromedevtools.github.io/timeline-viewer/?loadTimelineFromURL=https://gist.githubusercontent.com/ofrobots/807e41c6931726710cf7187680a095a7/raw/2843049b37145316204bb6fd7766b4f0e0b58989/request.trace) 12 | 13 | Click on the image above to go see an interactive version. 14 | 15 | ## Command Line Usage 16 | 17 | ```shell 18 | # Profiles the timeline of requiring `request@latest` and generates a trace 19 | # output file you can load in Chrome Timeline viewer [1] 20 | $ npx require-so-slow request 21 | 22 | # You can specify specific versions or dist-tags. 23 | $ npx require-so-slow got@9.0.0 24 | $ npx require-so-slow got@rc 25 | 26 | # You can specify what output filename to use: 27 | $ npm require-so-slow -o lodash.trace.json lodash 28 | ``` 29 | 30 | You can also preload require-so-slow from node: 31 | ```shell 32 | npm i -D require-so-slow 33 | 34 | # Traces the entire execution of entrypoint and writes to 35 | # ./require-so-slow.trace by default 36 | $ node -r require-so-slow [entrypoint] 37 | 38 | # The output path can be changed with the TRACE_OUTFILE environent variable 39 | ``` 40 | 41 | ## API 42 | 43 | ```js 44 | const requireSoSlow = require('require-so-slow'); 45 | 46 | // load stuff, run stuff. 47 | require('request'); 48 | 49 | // Write a trace file at some point. 50 | requireSoSlow.write('require-trace.trace'); 51 | ``` 52 | 53 | [Timeline Viewer]: https://chromedevtools.github.io/timeline-viewer/ 54 | -------------------------------------------------------------------------------- /src/perf-trace.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2017 The Bazel Authors. All rights reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * 8 | * You may obtain a copy of the License at 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | /** 19 | * perf_trace records traces in the Chrome Trace format (which is actually used 20 | * for more than just Chrome). See: 21 | * https://github.com/catapult-project/catapult/blob/master/tracing/README.md 22 | */ 23 | 24 | import * as fs from 'fs'; 25 | 26 | export type Microseconds = number; 27 | 28 | /** @return a high-res timestamp of the current time. */ 29 | export function now(): Microseconds { 30 | const [sec, nsec] = process.hrtime(); 31 | return sec * 1e6 + nsec / 1e3; 32 | } 33 | 34 | /** 35 | * The type of entries in the Chrome Trace format: 36 | * https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit 37 | * Field names are chosen to match the JSON format. 38 | */ 39 | export interface Event { 40 | name: string; 41 | ph: 'B' | 'E' | 'X' | 'C'; 42 | pid: number; // Required field in the trace viewer, but we don't use it. 43 | ts: Microseconds; 44 | dur?: Microseconds; 45 | args?: {}; 46 | } 47 | 48 | let events: Event[] = []; 49 | 50 | /** wrap wraps enter()/leave() calls around a block of code. */ 51 | export function wrap(name: string, f: () => T): T { 52 | const start = now(); 53 | try { 54 | return f(); 55 | } finally { 56 | const end = now(); 57 | events.push({name, ph: 'X', pid: 1, ts: start, dur: end - start}); 58 | } 59 | } 60 | 61 | /** 62 | * counter records a snapshot of counts. The counter name identifies a 63 | * single graph, while the counts object provides data for each count 64 | * of a line on the stacked bar graph. 65 | */ 66 | export function counter(name: string, counts: {[name: string]: number}) { 67 | events.push({name, ph: 'C', pid: 1, ts: now(), args: counts}); 68 | } 69 | 70 | /** write writes the trace in Chrome Trace format to a given path. */ 71 | export function write(path: string) { 72 | fs.writeFileSync(path, JSON.stringify(events), {encoding: 'utf8'}); 73 | } 74 | 75 | /** returns and clears the event buffer. */ 76 | export function getAndClearEvents(): Event[] { 77 | const buffer = events; 78 | events = []; 79 | return buffer; 80 | } 81 | 82 | /** Record the current heap usage to the performance trace. */ 83 | export function snapshotMemoryUsage() { 84 | const snapshot = process.memoryUsage(); 85 | // The counter displays as a stacked bar graph, so compute metrics 86 | // that sum to the appropriate total. 87 | const unused = snapshot.heapTotal - snapshot.heapUsed; 88 | counter('memory', {used: snapshot.heapUsed, unused}); 89 | } 90 | -------------------------------------------------------------------------------- /src/cli.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import {execSync} from 'child_process'; 4 | import * as path from 'path'; 5 | import * as shim from './shim'; 6 | import * as npa from 'npm-package-arg'; 7 | import * as up from 'update-notifier'; 8 | import * as meow from 'meow'; 9 | import fetch from 'node-fetch'; 10 | import * as tmp from 'tmp'; 11 | 12 | // eslint-disable-next-line @typescript-eslint/no-var-requires 13 | const MODULE = require('module'); 14 | 15 | // eslint-disable-next-line @typescript-eslint/no-var-requires 16 | const pkg = require('../../package.json'); 17 | up({pkg}).notify(); 18 | 19 | const cli = meow( 20 | ` 21 | Usage: 22 | $ rss [flags] 23 | 24 | Flags: 25 | -u, --upload Upload the trace to a public trace hosting service. 26 | -o, --output Write output to the specified file name. 27 | 28 | Examples: 29 | $ rss googleapis 30 | $ rss googleapis@20 31 | $ rss googleapis@28.1.0 32 | $ rss -o file.trace googleapis 33 | $ rss --upload request 34 | `, 35 | { 36 | autoHelp: true, 37 | autoVersion: true, 38 | flags: { 39 | upload: {type: 'boolean', alias: 'u'}, 40 | output: {type: 'string', alias: 'o'}, 41 | }, 42 | } 43 | ); 44 | 45 | if (cli.input.length !== 1) { 46 | cli.showHelp(1); 47 | } else if (cli.flags.upload && cli.flags.output) { 48 | console.error( 49 | 'Error: Only one of --upload/-u or --output/-o can be specified.' 50 | ); 51 | cli.showHelp(2); 52 | } 53 | 54 | function requireFromDirectory(request: string, directory: string) { 55 | const fakeParent = {paths: [path.join(directory, 'node_modules')]}; 56 | return MODULE._load(request, fakeParent, false); 57 | } 58 | 59 | function runInTmpDirectory(keep: boolean, fn: Function) { 60 | const dir = tmp.dirSync({keep, unsafeCleanup: true}); 61 | 62 | const origDir = process.cwd(); 63 | process.chdir(dir.name); 64 | 65 | const result = fn(dir.name); 66 | 67 | process.chdir(origDir); 68 | if (!keep) { 69 | dir.removeCallback(); 70 | } 71 | return result; 72 | } 73 | 74 | async function main() { 75 | const mod = cli.input[0]; 76 | const parsed = npa(mod); 77 | 78 | // TODO: add flag to allow users to run in local directory. 79 | // TODO: add flag to keep the temp directory. 80 | runInTmpDirectory(false, () => { 81 | execSync('npm init -y'); 82 | execSync(`npm install --no-save ${mod}`, {stdio: 'inherit'}); 83 | 84 | shim.createShim(); 85 | 86 | // Cannot use `require` here as it would consider *this file* to be the 87 | // parent and resolve relative to here. 88 | requireFromDirectory(parsed.name!, process.cwd()); 89 | }); 90 | 91 | if (cli.flags.upload) { 92 | if (!process.env.TRACE_SERVICE) { 93 | throw new Error( 94 | 'Env. var. TRACE_SERVICE needs to be defined for upload to work.' 95 | ); 96 | } 97 | // TODO: give warning to the user about the public upload. 98 | const events = shim.getAndClearEvents(); 99 | const result = await fetch(process.env.TRACE_SERVICE, { 100 | method: 'POST', 101 | body: JSON.stringify(events), 102 | headers: {'Content-Type': 'application/json'}, 103 | }); 104 | const json: {view: string} = await result.json(); 105 | console.info(`✨ Trace data uploaded. View at ${json.view}.`); 106 | // TODO: xdg-open the url. 107 | } else { 108 | let outputPath = cli.flags.output 109 | ? cli.flags.output 110 | : `${parsed.escapedName}.trace`; 111 | // Output for scoped packages like `@google-cloud/common` 112 | // came out with a %2f for the slash 113 | outputPath = outputPath.replace(/%2f/, '-'); 114 | const relativePath = path.relative(process.cwd(), outputPath); 115 | shim.write(outputPath); 116 | console.info(`✨ Trace data written to \`${relativePath}\` ✨`); 117 | console.info( 118 | 'To view, drop file in: https://chromedevtools.github.io/timeline-viewer/' 119 | ); 120 | } 121 | } 122 | 123 | main(); 124 | -------------------------------------------------------------------------------- /test/test-shim.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/no-var-requires 2 | const MODULE = require('module'); 3 | const ORIG_LOAD = MODULE._load; 4 | 5 | import {execSync} from 'child_process'; 6 | import {existsSync, mkdtempSync, readFileSync} from 'fs'; 7 | import {tmpdir} from 'os'; 8 | import {join, resolve} from 'path'; 9 | import * as test from 'tape'; 10 | import * as perfTrace from '../src/perf-trace'; 11 | import * as shim from '../src/shim'; 12 | 13 | function filterRequireEvent(events: perfTrace.Event[], path: string) { 14 | return events.filter(event => event.name === `require ${path}`); 15 | } 16 | 17 | test('No monkey patching before createShim is called', t => { 18 | t.equal(MODULE._load, ORIG_LOAD); 19 | t.end(); 20 | }); 21 | 22 | let instrumentedLoad: typeof MODULE._load; 23 | 24 | test('createShim should monkey patch', t => { 25 | shim.createShim(); 26 | t.notEqual(MODULE._load, ORIG_LOAD); 27 | instrumentedLoad = MODULE._load; 28 | t.end(); 29 | }); 30 | 31 | test('subsequent calls to createShim do not add additional monkey wraps', t => { 32 | shim.createShim(); 33 | t.equal(MODULE._load, instrumentedLoad); 34 | shim.createShim(); 35 | t.equal(MODULE._load, instrumentedLoad); 36 | t.end(); 37 | }); 38 | 39 | test('trace recording should have appropriate events', t => { 40 | const path = './fixtures/modA'; 41 | const t0 = perfTrace.now(); 42 | require(path); 43 | const t1 = perfTrace.now(); 44 | const matchingEvents = filterRequireEvent( 45 | perfTrace.getAndClearEvents(), 46 | path 47 | ); 48 | t.equal(matchingEvents.length, 1); 49 | const event = matchingEvents[0]; 50 | t.true(event.ts >= t0); 51 | t.true(event.ts <= t1); 52 | t.true(event.dur && event.dur <= t1 - t0); 53 | t.end(); 54 | }); 55 | 56 | test('trace recording should be accurate', t => { 57 | const path = './fixtures/modB'; 58 | perfTrace.now(); 59 | require(path); 60 | perfTrace.now(); 61 | const matchingEvents = filterRequireEvent( 62 | perfTrace.getAndClearEvents(), 63 | path 64 | ); 65 | t.equal(matchingEvents.length, 1); 66 | const event = matchingEvents[0]; 67 | t.true(event.dur && event.dur >= 2000); 68 | t.end(); 69 | }); 70 | 71 | test('modules already in cached do not show up in trace', t => { 72 | require('./fixtures/modC'); 73 | const events = perfTrace.getAndClearEvents(); 74 | t.equal(events.length, 1); 75 | t.end(); 76 | }); 77 | 78 | function inEmptyDir(fn: Function): void { 79 | const dir = mkdtempSync(join(tmpdir(), 'rss-test')); 80 | const prev = process.cwd(); 81 | process.chdir(dir); 82 | try { 83 | fn(); 84 | } finally { 85 | process.chdir(prev); 86 | } 87 | } 88 | 89 | test('preload traces from the start of the entrypoint and writes it to a file', t => { 90 | inEmptyDir(() => { 91 | const script = join(__dirname, 'fixtures', 'modA.js'); 92 | const rssPath = join(__dirname, '..', 'src', 'index.js'); 93 | const tracePath = resolve('require-so-slow.trace'); 94 | const nodePath = process.execPath; 95 | const command = `"${nodePath}" -r "${rssPath}" ${script}`; 96 | 97 | execSync(command); 98 | t.true(existsSync(tracePath)); 99 | 100 | const events: Array<{name: string}> = JSON.parse( 101 | readFileSync(tracePath, 'utf8') 102 | ); 103 | // Can't test that 'require /.../index.js' is the first event because nyc 104 | // hacks things into the node subprocess 105 | t.assert(events.find(e => e.name === `require ${resolve(script)}`)); 106 | t.end(); 107 | }); 108 | }); 109 | 110 | test('preload writes to a default path, otherwise to an env-specified directory', t => { 111 | inEmptyDir(() => { 112 | const script = join(__dirname, 'fixtures', 'modA.js'); 113 | const rssPath = join(__dirname, '..', 'src', 'index.js'); 114 | const tracePath = resolve('require-so-slow.trace'); 115 | const nodePath = process.execPath; 116 | const command = `"${nodePath}" -r "${rssPath}" ${script}`; 117 | 118 | execSync(command); 119 | t.true(existsSync(tracePath)); 120 | 121 | const envTracePath = resolve('rss.trace'); 122 | const command2 = `"${nodePath}" -r "${rssPath}" ${script}`; 123 | execSync(command2, { 124 | env: { 125 | TRACE_OUTFILE: envTracePath, 126 | }, 127 | }); 128 | t.true(existsSync(envTracePath)); 129 | t.end(); 130 | }); 131 | }); 132 | 133 | test.skip('record a trace for a module that throws', () => {}); 134 | 135 | test.skip('record a trace for a non-existent module', () => {}); 136 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ### [2.0.5](https://www.github.com/GoogleCloudPlatform/require-so-slow/compare/v2.0.4...v2.0.5) (2021-01-10) 4 | 5 | 6 | ### Bug Fixes 7 | 8 | * **deps:** update dependency meow to v9 ([#85](https://www.github.com/GoogleCloudPlatform/require-so-slow/issues/85)) ([c8e1c62](https://www.github.com/GoogleCloudPlatform/require-so-slow/commit/c8e1c62dd06110703c70abdb6e0e1865757f1e2e)) 9 | 10 | ### [2.0.4](https://www.github.com/GoogleCloudPlatform/require-so-slow/compare/v2.0.3...v2.0.4) (2020-10-29) 11 | 12 | 13 | ### Bug Fixes 14 | 15 | * **deps:** update dependency meow to v8 ([#82](https://www.github.com/GoogleCloudPlatform/require-so-slow/issues/82)) ([a908d9c](https://www.github.com/GoogleCloudPlatform/require-so-slow/commit/a908d9c5f8a5189f73956e3883e7e275e0001942)) 16 | 17 | ### [2.0.3](https://www.github.com/GoogleCloudPlatform/require-so-slow/compare/v2.0.2...v2.0.3) (2020-09-29) 18 | 19 | 20 | ### Bug Fixes 21 | 22 | * **deps:** update dependency update-notifier to v5 ([#78](https://www.github.com/GoogleCloudPlatform/require-so-slow/issues/78)) ([0d63a51](https://www.github.com/GoogleCloudPlatform/require-so-slow/commit/0d63a516c43598a236c957439b32e81ceab18d8a)) 23 | 24 | ### [2.0.2](https://www.github.com/GoogleCloudPlatform/require-so-slow/compare/v2.0.1...v2.0.2) (2020-05-18) 25 | 26 | 27 | ### Bug Fixes 28 | 29 | * **deps:** update dependency meow to v7 ([#75](https://www.github.com/GoogleCloudPlatform/require-so-slow/issues/75)) ([0a66336](https://www.github.com/GoogleCloudPlatform/require-so-slow/commit/0a6633652a07fd27b1dda85d525cbfa13ebbc643)) 30 | 31 | ### [2.0.1](https://www.github.com/GoogleCloudPlatform/require-so-slow/compare/v2.0.0...v2.0.1) (2020-04-29) 32 | 33 | 34 | ### Bug Fixes 35 | 36 | * **deps:** update dependency tmp to v0.2.0 ([#69](https://www.github.com/GoogleCloudPlatform/require-so-slow/issues/69)) ([3ddd571](https://www.github.com/GoogleCloudPlatform/require-so-slow/commit/3ddd5714b3efb85be11f26ba81da690f49610af1)) 37 | * **deps:** update dependency tmp to v0.2.1 ([#71](https://www.github.com/GoogleCloudPlatform/require-so-slow/issues/71)) ([2385ba3](https://www.github.com/GoogleCloudPlatform/require-so-slow/commit/2385ba32f933d642805f5456278d8caab8d5ae61)) 38 | * improve output file name for scoped packages ([#72](https://www.github.com/GoogleCloudPlatform/require-so-slow/issues/72)) ([3506da5](https://www.github.com/GoogleCloudPlatform/require-so-slow/commit/3506da5e635917e4f4c8abd22ee65ec19bedc0cb)) 39 | 40 | ## [2.0.0](https://www.github.com/GoogleCloudPlatform/require-so-slow/compare/v1.2.0...v2.0.0) (2020-04-15) 41 | 42 | 43 | ### ⚠ BREAKING CHANGES 44 | 45 | * Node.js is reaching EOL. Stop supporting it. 46 | * The CLI format has changed. 47 | 48 | ### Features 49 | 50 | * ability to upload traces ([#19](https://www.github.com/GoogleCloudPlatform/require-so-slow/issues/19)) ([4f0591e](https://www.github.com/GoogleCloudPlatform/require-so-slow/commit/4f0591e1cfc5e54660cba2bf8448dd9182a31d62)) 51 | * add update-notifier ([#13](https://www.github.com/GoogleCloudPlatform/require-so-slow/issues/13)) ([bcd2aaf](https://www.github.com/GoogleCloudPlatform/require-so-slow/commit/bcd2aaf3d1aa305a35fd83fb1a79967d9a5ea20c)) 52 | * npx support. ([#17](https://www.github.com/GoogleCloudPlatform/require-so-slow/issues/17)) ([b58d0b6](https://www.github.com/GoogleCloudPlatform/require-so-slow/commit/b58d0b6ff35623fa18b7d3d3f68cf38588ae9061)) 53 | * preload require support ([#50](https://www.github.com/GoogleCloudPlatform/require-so-slow/issues/50)) ([27a0ddd](https://www.github.com/GoogleCloudPlatform/require-so-slow/commit/27a0dddd98fbbd65fec98d2e6af9088967be2c55)) 54 | * provide a cli ([#2](https://www.github.com/GoogleCloudPlatform/require-so-slow/issues/2)) ([4b34bc6](https://www.github.com/GoogleCloudPlatform/require-so-slow/commit/4b34bc6474fe9241ab266ae7f55c8ecba03115c5)) 55 | * sandbox run into a tmp directory ([#21](https://www.github.com/GoogleCloudPlatform/require-so-slow/issues/21)) ([b1d027f](https://www.github.com/GoogleCloudPlatform/require-so-slow/commit/b1d027fe3d673cf1fb00080361445a6e060d2b61)) 56 | 57 | 58 | ### Bug Fixes 59 | 60 | * audit fix ([#38](https://www.github.com/GoogleCloudPlatform/require-so-slow/issues/38)) ([10c53e7](https://www.github.com/GoogleCloudPlatform/require-so-slow/commit/10c53e77bf3c41ae30f7026193029fceee798640)) 61 | * include node-fetch as a dependency ([#23](https://www.github.com/GoogleCloudPlatform/require-so-slow/issues/23)) ([13d68f9](https://www.github.com/GoogleCloudPlatform/require-so-slow/commit/13d68f9fb21b4a8154e4333761696225c9e8bf39)) 62 | * require relative to working dir ([#20](https://www.github.com/GoogleCloudPlatform/require-so-slow/issues/20)) ([aacde1f](https://www.github.com/GoogleCloudPlatform/require-so-slow/commit/aacde1fa4d5089f78791de980f26f25b3640e905)) 63 | * upgrade all dependencies ([#16](https://www.github.com/GoogleCloudPlatform/require-so-slow/issues/16)) ([acd23a2](https://www.github.com/GoogleCloudPlatform/require-so-slow/commit/acd23a2df145c3c22bc0e3309fcf638f92cb9bb8)) 64 | * **deps:** update dependency meow to v6 ([#54](https://www.github.com/GoogleCloudPlatform/require-so-slow/issues/54)) ([8d61a2e](https://www.github.com/GoogleCloudPlatform/require-so-slow/commit/8d61a2ef60b934943de7d9ab567420a811e2094e)) 65 | * **deps:** update dependency npm-package-arg to v7 ([#53](https://www.github.com/GoogleCloudPlatform/require-so-slow/issues/53)) ([7b6cbe2](https://www.github.com/GoogleCloudPlatform/require-so-slow/commit/7b6cbe2c31409f128d2656008e6b65770d511232)) 66 | * **deps:** update dependency npm-package-arg to v8 ([#56](https://www.github.com/GoogleCloudPlatform/require-so-slow/issues/56)) ([ae6dddb](https://www.github.com/GoogleCloudPlatform/require-so-slow/commit/ae6dddb483942bd7382418200ff9b82a6d803d3f)) 67 | * **deps:** update dependency tmp to v0.1.0 ([#35](https://www.github.com/GoogleCloudPlatform/require-so-slow/issues/35)) ([0f2dcee](https://www.github.com/GoogleCloudPlatform/require-so-slow/commit/0f2dceef80061762a2aa540ed0c66cd4e4a1ac90)) 68 | * **deps:** update dependency update-notifier to v3 ([#42](https://www.github.com/GoogleCloudPlatform/require-so-slow/issues/42)) ([c01d916](https://www.github.com/GoogleCloudPlatform/require-so-slow/commit/c01d916e4361452a4bac70c52a29a77009c083f0)) 69 | * **deps:** update dependency update-notifier to v4 ([#55](https://www.github.com/GoogleCloudPlatform/require-so-slow/issues/55)) ([8c9f454](https://www.github.com/GoogleCloudPlatform/require-so-slow/commit/8c9f454b77dc0bf61778a24ad8243500dc5fe8f6)) 70 | 71 | 72 | ### Build System 73 | 74 | * drop support for Node 6 ([#41](https://www.github.com/GoogleCloudPlatform/require-so-slow/issues/41)) ([56c271c](https://www.github.com/GoogleCloudPlatform/require-so-slow/commit/56c271c9b39667e3213e99f0b281238cb4bf7e82)) 75 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. --------------------------------------------------------------------------------