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:
if
enumerable
exists, it must betrue
in descriptorcontains
value
orgetter
; a. ifvalue
exists in descriptor b. ifgetter
exists, function must returnIdentifier
orMemberExpression
orStringLiteral
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