Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Commit

Permalink
Ensure fs.Stats (and other classes on fs) are not mutated.
Browse files Browse the repository at this point in the history
`fs.Stats` is a standard prototypal class (e.g. `function Stats() {}`),
so it was being wrapped by our `fs` monitoring system. Unfortunately,
wrapping `fs.Stats` in a plain function (which did `return
original.apply(this, arguments)`) doesn't properly emulate a number of
"standard" class behaviors. Specifically:

* static methods and properties are lost
* accessing `fs.Stats.prototype` does not refer to the original prototype

This last item was causing the following error when using `esm`:

```
TypeError: Function.prototype.apply was called on undefined, which is a undefined and not a function
    at $o (/home/travis/build/ember-cli/ember-cli/tmp/node-FGpJBujF.tmp/node_modules/esm/esm.js:1:224377)
    at xu (/home/travis/build/ember-cli/ember-cli/tmp/node-FGpJBujF.tmp/node_modules/esm/esm.js:1:226413)
    at wu (/home/travis/build/ember-cli/ember-cli/tmp/node-FGpJBujF.tmp/node_modules/esm/esm.js:1:227391)
    at Eu (/home/travis/build/ember-cli/ember-cli/tmp/node-FGpJBujF.tmp/node_modules/esm/esm.js:1:227999)
    at Module.<anonymous> (/home/travis/build/ember-cli/ember-cli/tmp/node-FGpJBujF.tmp/node_modules/esm/esm.js:1:295976)
    at n (/home/travis/build/ember-cli/ember-cli/tmp/node-FGpJBujF.tmp/node_modules/esm/esm.js:1:279589)
    at getFeatures (/home/travis/build/ember-cli/ember-cli/tmp/node-FGpJBujF.tmp/node_modules/@ember-data/-build-infra/src/features.js:5:33)
    at Object.<anonymous> (/home/travis/build/ember-cli/ember-cli/tmp/node-FGpJBujF.tmp/node_modules/@ember-data/-build-infra/src/features.js:31:18)
```

This is because `esm` is _essentially_ doing:

```js
let isFile = fs.Stats.prototype.isFile;
```

To stash off the `isFile` implementation for recurring usage. The fact that when wrapped `fs.Stat.prototype.isFile` was undefined caused the error.
  • Loading branch information
rwjblue committed Aug 19, 2019
1 parent 62008b4 commit a786a7d
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 5 deletions.
10 changes: 9 additions & 1 deletion index.js
Expand Up @@ -13,7 +13,15 @@ let hasActiveInstance = false;
class FSMonitor {
constructor() {
this.state = 'idle';
this.blacklist = ['createReadStream', 'createWriteStream', 'ReadStream', 'WriteStream'];
this.blacklist = [
'createReadStream',
'createWriteStream',
'Dirent',
'FSWatcher',
'ReadStream',
'Stats',
'WriteStream'
];
}

start() {
Expand Down
57 changes: 53 additions & 4 deletions tests.js
Expand Up @@ -3,7 +3,8 @@
const FSMonitor = require('./');
const expect = require('chai').expect;
const fs = require('fs');
const originalStatSync = fs.statSync;

const originalFS = Object.assign({}, fs);

describe('FSMonitor', function() {
it('will only allow one active instance at a time', function() {
Expand All @@ -28,17 +29,65 @@ describe('FSMonitor', function() {
monitor0.stop();
});

it('does not mutate the prototype of classes on fs [GH#22]', function() {
let monitor = new FSMonitor();

expect(typeof fs.Stats.prototype.isFile).to.equal('function');

monitor.start();

expect(typeof fs.Stats.prototype.isFile).to.equal('function', 'after updating fs');

monitor.stop();

expect(typeof fs.Stats.prototype.isFile).to.equal('function');
});

it('avoids mutating known classes on `fs` [GH#22]', function() {
let monitor = new FSMonitor();

expect(fs.Stats.prototype.isFile).to.be;
expect(fs.Stats).to.equal(originalFS.Stats);
expect(fs.Dirent).to.equal(originalFS.Dirent);
expect(fs.FSWatcher).to.equal(originalFS.FSWatcher);
expect(fs.FileHandle).to.equal(originalFS.FileHandle);
expect(fs.ReadStream).to.equal(originalFS.ReadStream);
expect(fs.WriteStream).to.equal(originalFS.WriteStream);

try {
monitor.start();

// should not have been changed
expect(fs.Stats).to.equal(originalFS.Stats);
expect(fs.Dirent).to.equal(originalFS.Dirent);
expect(fs.FSWatcher).to.equal(originalFS.FSWatcher);
expect(fs.FileHandle).to.equal(originalFS.FileHandle);
expect(fs.ReadStream).to.equal(originalFS.ReadStream);
expect(fs.WriteStream).to.equal(originalFS.WriteStream);
} finally {
// ensure we stop and detach even if we fail an assertion
monitor.stop();
}

expect(fs.Stats).to.equal(originalFS.Stats);
expect(fs.Dirent).to.equal(originalFS.Dirent);
expect(fs.FSWatcher).to.equal(originalFS.FSWatcher);
expect(fs.FileHandle).to.equal(originalFS.FileHandle);
expect(fs.ReadStream).to.equal(originalFS.ReadStream);
expect(fs.WriteStream).to.equal(originalFS.WriteStream);
});

describe('.prototype.stop', function() {
it('restores fs functions to their defaults', function() {
let monitor = new FSMonitor();

expect(fs.statSync).to.equal(originalStatSync);
expect(fs.statSync).to.equal(originalFS.statSync);

monitor.start();
expect(fs.statSync).to.not.equal(originalStatSync);
expect(fs.statSync).to.not.equal(originalFS.statSync);

monitor.stop();
expect(fs.statSync).to.equal(originalStatSync);
expect(fs.statSync).to.equal(originalFS.statSync);
});
});
});

0 comments on commit a786a7d

Please sign in to comment.