Skip to content

Commit

Permalink
Capture module objects when function declaration called module at…
Browse files Browse the repository at this point in the history
… top level [fix]

Fixes #566.
  • Loading branch information
overlookmotel committed Dec 23, 2023
1 parent c3915c7 commit d35881f
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 9 deletions.
9 changes: 3 additions & 6 deletions lib/init/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
// Imports
const createTracker = require('./tracker.js'),
getScopeId = require('./getScopeId.js'),
internal = require('../shared/internal.js'),
{COMMON_JS_MODULE} = require('../shared/constants.js');
internal = require('../shared/internal.js');

// Exports

Expand All @@ -26,15 +25,13 @@ const {globals, functions: specialFunctions} = internal;
* Additional methods which are universal are attached to `livepack_getScopeId`.
*
* @param {string} filename - File path
* @param {Object} module - `module` object from file
* @param {Function} require - `require` function from file
* @param {number} nextBlockId - Next block ID
* @param {number} prefixNum - Internal vars prefix num
* @returns {Array<Function>} - Array containing tracker and `getScopeId` functions
*/
module.exports = (filename, module, require, nextBlockId, prefixNum) => {
// Record `module` + `require`
globals.set(module, {type: COMMON_JS_MODULE, parent: null, key: 'module'});
module.exports = (filename, require, nextBlockId, prefixNum) => {
// Record `require`
specialFunctions.set(require, {type: 'require', path: filename});
specialFunctions.set(require.resolve, {type: 'require', path: filename});
specialFunctions.set(require.resolve.paths, {type: 'require', path: filename});
Expand Down
12 changes: 11 additions & 1 deletion lib/init/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ const Module = require('module');
// Imports
const {catalogBuiltInModule} = require('./globals.js'),
{usingInternalModuleCache} = require('../shared/moduleCache.js'),
{globals, functions: specialFunctions} = require('../shared/internal.js');
{globals, functions: specialFunctions} = require('../shared/internal.js'),
{COMMON_JS_MODULE} = require('../shared/constants.js');

// Exports

Expand All @@ -36,6 +37,15 @@ module.exports = function patchModule() {
return exports;
};
Module.prototype.require.main = requireOriginal.main;

// Patch `Module.prototype.load` to record `module` object
const loadOriginal = Module.prototype.load;
Module.prototype.load = function(filename) {
if (!usingInternalModuleCache()) {
globals.set(this, {type: COMMON_JS_MODULE, parent: null, key: 'module'});
}
loadOriginal.call(this, filename);
};
};

/**
Expand Down
3 changes: 1 addition & 2 deletions lib/instrument/modify.js
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ function insertImportStatement(programNode, state) {
// ```
// const [livepack_tracker, livepack_getScopeId]
// = require('/path/to/app/node_modules/livepack/lib/init/index.js')
// ('/path/to/app/file.js', module, require, 100, 0);
// ('/path/to/app/file.js', require, 100, 0);
// ```
const statementNode = t.variableDeclaration(
'const', [
Expand All @@ -243,7 +243,6 @@ function insertImportStatement(programNode, state) {
t.callExpression(t.identifier('require'), [t.stringLiteral(INIT_PATH)]),
[
t.stringLiteral(state.filename),
t.identifier('module'),
t.identifier('require'),
t.numericLiteral(state.nextBlockId),
t.numericLiteral(state.internalVarsPrefixNum)
Expand Down
32 changes: 32 additions & 0 deletions test/commonjs.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,38 @@ describe('`module`', () => {
expect(fn()).toBe(123);
}
});

itSerializes('var overriden by function declaration and module object exported', {
in: `
'use strict';
function module() { return 123; }
// arguments[2] is module object
arguments[2].exports = arguments[2];
`,
out: '(()=>{const a={};a.exports=a;return a})()',
validate(mod, {isOutput}) {
expect(mod).toBeObject();
if (isOutput) expect(mod).toHaveOwnPropertyNames(['exports']);
expect(mod.exports).toBe(mod);
}
});

itSerializes('var overriden by function declaration and function exported', {
in: `
'use strict';
function module() { return 123; }
exports.x = module;
`,
out: '{x:function module(){return 123}}',
validate(obj) {
expect(obj).toBeObject();
expect(obj).toHaveOwnPropertyNames(['x']);
const fn = obj.x;
expect(fn).toBeFunction();
expect(fn.name).toBe('module');
expect(fn()).toBe(123);
}
});
});

describe('`exports`', () => {
Expand Down

0 comments on commit d35881f

Please sign in to comment.