npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

cjs-exports-parser

v0.1.1

Published

Parse cjs exports

Downloads

8

Readme

cjs-exports-parser

Parse cjs exports

Usage

npm install cjs-exports-parser
import { parseCode } from 'cjs-exports-parser';

const { exports, reexports } = parseCode(`
  // named exports detection
  module.exports.a = 'a';
  (function () {
    exports.b = 'b';
  })();
  Object.defineProperty(exports, 'c', { value: 'c' });
  /* exports.d = 'not detected'; */

  // reexports detection
  if (maybe) module.exports = require('./dep1.js');
  if (another) module.exports = require('./dep2.js');

  // literal exports assignments
  module.exports = { a, b: c, d, 'e': f }

  // __esModule detection
  Object.defineProperty(module.exports, '__esModule', { value: true })
`);

or

import { parseFile } from 'cjs-exports-parser';

const { exports } = await parseFile('/absolute/path/to/your/file.js');

parseFile does not return reexports, because it automaticlly invokes recurrsively to collect exports.

Cases

Named Exports Parsing

// DETECTS EXPORTS: a, b
(function (exports) {
  exports.a = 'a';
  exports['b'] = 'b';
})(exports);

❗️Could not opt-out property in falsy branch:

exports.a = 'a';
exports['b'] = 'b';
if (false) exports.c = 'c';

❗️Could not opt-out exports as param name explicitly:

// DETECTS EXPORTS: a, b, c
(function (exports, Object) {
  exports.a = 'a';
  exports['b'] = 'b';
  if (false) exports.c = 'c';
})(NOT_EXPORTS, NOT_OBJECT);

Could detects exports as function parameter and use another formal variable name:

// DETECTS: a, b
(function (e) {
  e.a = 'a';
  e['b'] = 'b';
})(exports);
// DETECTS EXPORTS: NO EXPORTS
(function (exports, Object) {
  exports.a = 'a';
  exports['b'] = 'b';
  if (false) exports.c = 'c';
})(NOT_EXPORTS, NOT_OBJECT);

Getter Exports Parsing

Object.defineProperty are suppored, in this following cases the property will be taken into exports:

  1. if enumerable exists, it must be true in descriptor

  2. contains value or getter; a. if value exists in descriptor b. if getter exists, function must return Identifier or MemberExpression or StringLiteral

  3. no other properties exists in descriptor

// DETECTS: a, b, c, d, e, __esModule
Object.defineProperty(exports, 'a', {
  enumerable: true,
  get: function () {
    return q.p;
  },
});
Object.defineProperty(exports, 'b', {
  enumerable: true,
  get: function () {
    return q['p'];
  },
});
Object.defineProperty(exports, 'c', {
  enumerable: true,
  get() {
    return b;
  },
});
Object.defineProperty(exports, 'd', { value: 'd' });
Object.defineProperty(exports, 'e', {
  enumerable: true,
  get() {
    return 'str';
  },
});
Object.defineProperty(exports, '__esModule', { value: true });

❗️To avoid matching getters that have side effects, any getter for an export name that does not support the forms above will opt-out of the getter matching:

// DETECTS: NO EXPORTS
Object.defineProperty(exports, 'a', {
  get() {
    return 'nope';
  },
});

if (false) {
  Object.defineProperty(module.exports, 'a', {
    get() {
      return dynamic();
    },
  });
}

❗️Alternative object definition structures or getter function bodies are not detected:

// DETECTS: NO EXPORTS
Object.defineProperty(exports, 'a', {
  enumerable: false,
  get() {
    return p;
  },
});
Object.defineProperty(exports, 'b', {
  configurable: true,
  get() {
    return p;
  },
});
Object.defineProperty(exports, 'c', {
  get: () => p,
});
Object.defineProperty(exports, 'd', {
  enumerable: true,
  get: function () {
    return dynamic();
  },
});

❗️Object.defineProperties is also not supported.

Exports Object Parsing

❗️spreads are ignored

// DETECTS EXPORTS: a, b, c
module.exports = {
  a,
  b: b,
  c: c,
  ...d,
};
// DETECTS EXPORTS: a, b, c
module.exports = {
  a,
  ...d,
  b: require('c'),
  c: 'c',
};

module.exports reexport assignment

Any module.exports = require('mod') assignment is detected as a reexport, but only the last one is returned:

// DETECTS REEXPORTS: c
module.exports = require('a');
((module) => (module.exports = require('b')))(NOT_MODULE);
if (false) module.exports = require('c');
// DETECTS REEXPORTS: a, b
module.exports = require('ignored');
module.exports = {
  ...require('a'),
  ...require('b'),
};

Transpiler reexports

babel, TS and other bundlers would like to inject reexport helper functions

babel:

// DETECT REEXPORTS: 'external'
'use strict';

exports.__esModule = true;

var _external = require('external');

Object.keys(_external).forEach(function (key) {
  if (key === 'default' || key === '__esModule') return;
  exports[key] = _external[key];
});

TS:

'use strict';
var __createBinding =
  (this && this.__createBinding) ||
  (Object.create
    ? function (o, m, k, k2) {
        if (k2 === undefined) k2 = k;
        Object.defineProperty(o, k2, {
          enumerable: true,
          get: function () {
            return m[k];
          },
        });
      }
    : function (o, m, k, k2) {
        if (k2 === undefined) k2 = k;
        o[k2] = m[k];
      });
var __exportStar =
  (this && this.__exportStar) ||
  function (m, exports) {
    for (var p in m)
      if (p !== 'default' && !exports.hasOwnProperty(p))
        __createBinding(exports, m, p);
  };
Object.defineProperty(exports, '__esModule', { value: true });
__exportStar(require('./foo'), exports);

NOTE

Normally, the results are more accurate than cjs-module-lexer since cjs-exports-parser do further analysis via babel, but if you do care about performance pretty much, please give cjs-module-lexer a try.

LICENSE

MIT