Skip to content

Commit

Permalink
Merge pull request #36 from dumberjs/exports
Browse files Browse the repository at this point in the history
fix: fix local require check in npm package
  • Loading branch information
3cp authored Dec 22, 2024
2 parents e313a0c + fba127f commit fc8f0fe
Show file tree
Hide file tree
Showing 6 changed files with 2,024 additions and 1,614 deletions.
6 changes: 2 additions & 4 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ module.exports = class Bundler {
const checkUserSpace = opts.user;
const checkPackageSpace = opts.package;
const requiredBy = opts.requiredBy;
const isRelative = opts.isRelative;
const parsedId = parse(mapId(id, this._paths));

if (parsedId.bareId.match(/^(?:https?:)?\//)) {
Expand Down Expand Up @@ -338,11 +339,8 @@ module.exports = class Bundler {
});
}

const parsedRequiredBy = requiredBy.map(id => parse(mapId(id, this._paths)));
const isLocalRequire = parsedRequiredBy.findIndex(parsed => parsed.parts[0] === packageName) !== -1;

return this.packageReaderFor(stub || {name: packageName})
.then(reader => resource ? reader.readResource(resource, isLocalRequire) : reader.readMain())
.then(reader => resource ? reader.readResource(resource, isRelative) : reader.readMain())
.then(unit => this.capture(unit))
.catch(err => {
error('Resolving failed for module ' + bareId);
Expand Down
23 changes: 9 additions & 14 deletions lib/modules-todo.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ module.exports = class ModulesTodo {
const {moduleId, packageName, deps} = unit;

deps.forEach(d => {
const isRelative = parse(d).bareId.startsWith('.') ? 1 : 0;
const parsedId = parse(resolveModuleId(moduleId, d));

if (!parsedId.prefix && (
Expand All @@ -41,7 +42,7 @@ module.exports = class ModulesTodo {
let space;
if (packageName) {
space = PACKAGE;
} else if (isRelative(d)) {
} else if (isRelative) {
// local relative
space = USER;
} else {
Expand All @@ -58,12 +59,13 @@ module.exports = class ModulesTodo {

let pluginSpace = space;
if (space !== PACKAGE) pluginSpace = USER_OR_PACKAGE;
const key = pluginSpace + ':' + pluginName;
const isRelativePlugin = pluginName.startsWith('.') ? 1 : 0;
const key = pluginSpace + ':' + pluginName + ':' + isRelativePlugin;
if (!this.todos[key]) this.todos[key] = [];
this.todos[key].push(moduleId);
}

const key = space + ':' + parsedId.cleanId;
const key = space + ':' + parsedId.cleanId + ':' + isRelative;
if (!this.todos[key]) this.todos[key] = [];
this.todos[key].push(moduleId);
});
Expand All @@ -85,7 +87,7 @@ module.exports = class ModulesTodo {
groups[SERIAL_GROUP] = [];

keys.forEach(key => {
const [space, id] = spaceAndId(key);
const [space, id] = key.split(':');

// Don't need the parallel optimization when running
// in nodejs.
Expand All @@ -106,25 +108,18 @@ module.exports = class ModulesTodo {
let p = Promise.resolve();

keys.forEach(key => {
const [space, id] = spaceAndId(key);
const [space, id, isRelative] = key.split(':');
const requiredBy = todos[key];

p = p.then(() => cb(id, {
user: space !== PACKAGE,
package: space !== USER,
requiredBy
requiredBy,
isRelative: isRelative === '1'
}));
});

return p;
}));
}
};

function spaceAndId(key) {
return [key.slice(0, 1), key.slice(2)];
}

function isRelative(id) {
return parse(id).bareId.startsWith('.');
}
265 changes: 172 additions & 93 deletions test/bundler.esm.spec.js
Original file line number Diff line number Diff line change
@@ -1,118 +1,197 @@
const {test} = require('zora');
const Bundler = require('../lib/index');
const {contentOrFile} = require('../lib/shared');
const {mockResolve, buildReadFile, mockPackageFileReader} = require('./mock');
const { test } = require("zora");
const { mockBundler } = require("./mock");

function mockContentOrFile(fakeReader) {
return pathOrContent => contentOrFile(pathOrContent, {readFile: fakeReader});
}

function deleteSourceMap(file) {
delete file.sourceMap;
}

function createBundler(fakeFs = {}, opts = {}) {
// don't use cache in test
if (!opts.cache) opts.cache = false;
const fakeReader = buildReadFile(fakeFs);
opts.packageFileReader = mockPackageFileReader(fakeReader);

const bundler = new Bundler(opts, {
resolve: mockResolve,
contentOrFile: mockContentOrFile(fakeReader)
test("Bundler traces mixed mjs and cjs npm packages", async (t) => {
const fakeFs = {
"local/setup.js": "setup",
"local/after.js": "after",
"node_modules/dumber-module-loader/dist/index.debug.js":
"dumber-module-loader",
"node_modules/foo/package.json": JSON.stringify({
name: "foo",
main: "index.cjs",
}),
"node_modules/foo/index.cjs": "require('loo');",
"node_modules/loo/package.json": JSON.stringify({
name: "loo",
main: "./loo.mjs",
}),
"node_modules/loo/loo.mjs": "",
};
const bundler = mockBundler(fakeFs, {
prepends: ["var pre = 1;", "", undefined, false, "local/setup.js", null],
appends: ["local/after.js", "var ape = 1;"],
});

const oldBundle = bundler.bundle.bind(bundler);
bundler.bundle = function() {
// don't test source map
const bundleMap = oldBundle();
Object.keys(bundleMap).forEach(key => {
if (bundleMap[key].files) {
bundleMap[key].files.forEach(deleteSourceMap);
}
if (bundleMap[key].appendFiles) {
bundleMap[key].appendFiles.forEach(deleteSourceMap);
}
});
return bundleMap;
};
return bundler;
}
return Promise.resolve()
.then(() =>
bundler.capture({
path: "src/app.js",
contents: "require('foo');",
moduleId: "app.js",
})
)
.then(() => bundler.resolve())
.then(() => bundler.bundle())
.then(
(bundleMap) => {
t.deepEqual(bundleMap, {
"entry-bundle": {
files: [
{
contents: "var pre = 1;",
},
{
path: "local/setup.js",
contents: "setup;",
},
{
path: "node_modules/dumber-module-loader/dist/index.debug.js",
contents: "dumber-module-loader;",
},
{
contents: "define.switchToUserSpace();",
},
{
path: "src/app.js",
contents:
"define('app.js',['require','exports','module','foo'],function (require, exports, module) {\nrequire('foo');\n});\n",
},
{
contents: "define.switchToPackageSpace();",
},
{
path: "node_modules/foo/index.cjs",
contents:
"define('foo/index.cjs',['require','exports','module','loo'],function (require, exports, module) {\nrequire('loo');\n});\n\n;define.alias('foo','foo/index.cjs');",
},
{
path: "node_modules/loo/loo.mjs",
contents:
"define('loo/loo.mjs',['require','exports','module'],function (require, exports, module) {\n\n});\n\n;define.alias('loo','loo/loo.mjs');",
},
{
contents: "define.switchToUserSpace();",
},
],
appendFiles: [
{
path: "local/after.js",
contents: "after;",
},
{
contents: "var ape = 1;",
},
],
config: {
baseUrl: "/dist",
paths: {},
bundles: {},
},
},
});
},
(err) => t.fail(err.stack)
);
});

test('Bundler traces mixed mjs and cjs npm packages', async t => {
test("Bundler bundles npm package with exports field", async (t) => {
const fakeFs = {
'local/setup.js': 'setup',
'local/after.js': 'after',
'node_modules/dumber-module-loader/dist/index.debug.js': 'dumber-module-loader',
'node_modules/foo/package.json': JSON.stringify({name: 'foo', main: 'index.cjs'}),
'node_modules/foo/index.cjs': "require('loo');",
'node_modules/loo/package.json': JSON.stringify({name: 'loo', main: './loo.mjs'}),
'node_modules/loo/loo.mjs': '',
"node_modules/dumber-module-loader/dist/index.debug.js":
"dumber-module-loader",
"node_modules/esm-env/package.json": JSON.stringify({
name: "esm-env",
type: "module",
exports: {
".": {
types: "./index.d.ts",
default: "./index.js",
},
"./browser": {
browser: "./true.js",
development: "./false.js",
production: "./false.js",
default: "./browser-fallback.js",
},
"./development": {
development: "./true.js",
production: "./false.js",
default: "./dev-fallback.js",
},
"./node": {
node: "./true.js",
default: "./false.js",
},
},
}),
"node_modules/esm-env/index.js": `export { default as BROWSER } from 'esm-env/browser';
export { default as DEV } from 'esm-env/development';
export { default as NODE } from 'esm-env/node';
`,
"node_modules/esm-env/true.js": "export default true;\n",
"node_modules/esm-env/false.js": "export default false;\n",
"node_modules/esm-env/dev-fallback.js":
"export default typeof DEV !== 'undefined';\n",
};
const bundler = createBundler(fakeFs, {
prepends: ['var pre = 1;', '', undefined, false, 'local/setup.js', null],
appends: ['local/after.js', 'var ape = 1;']

const bundler = mockBundler(fakeFs, {
onRequire: (id) => {
if (id.startsWith("tslib")) {
return false;
}
},
});

return Promise.resolve()
.then(() => bundler.capture({path: 'src/app.js', contents: "require('foo');", moduleId: 'app.js'}))
.then(() => bundler.resolve())
.then(() => bundler.bundle())
.then(
bundleMap => {
.then(() =>
bundler.capture({
path: "src/app.js",
contents:
"import {BROWSER, DEV, NODE} from 'esm-env';\nconsole.log(BROWSER, DEV, NODE);\n",
moduleId: "app.js",
})
)
.then(() => bundler.resolve())
.then(() => bundler.bundle())
.then((bundleMap) => {
t.deepEqual(bundleMap, {
"entry-bundle": {
"files": [
{
"contents": "var pre = 1;"
},
{
"path": "local/setup.js",
"contents": "setup;"
},
{
"path": "node_modules/dumber-module-loader/dist/index.debug.js",
"contents": "dumber-module-loader;"
},
files: [
{
"contents": "define.switchToUserSpace();"
path: "node_modules/dumber-module-loader/dist/index.debug.js",
contents: "dumber-module-loader;",
},
{ contents: "define.switchToUserSpace();" },
{
"path": "src/app.js",
"contents": "define('app.js',['require','exports','module','foo'],function (require, exports, module) {\nrequire('foo');\n});\n"
path: "src/app.js",
contents:
"define('app.js',['require','exports','module','esm-env'],function (require, exports, module) {\n\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst esm_env_1 = require(\"esm-env\");\nconsole.log(esm_env_1.BROWSER, esm_env_1.DEV, esm_env_1.NODE);\n\n});\n",
},
{ contents: "define.switchToPackageSpace();" },
{
"contents": "define.switchToPackageSpace();"
path: "node_modules/esm-env/dev-fallback.js",
contents:
"define('esm-env/dev-fallback.js',['require','exports','module'],function (require, exports, module) {\n\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.default = typeof DEV !== 'undefined';\n\n});\n\n;define.alias('esm-env/development','esm-env/dev-fallback.js');",
},
{
"path": "node_modules/foo/index.cjs",
"contents": "define('foo/index.cjs',['require','exports','module','loo'],function (require, exports, module) {\nrequire('loo');\n});\n\n;define.alias('foo','foo/index.cjs');"
path: "node_modules/esm-env/false.js",
contents:
"define('esm-env/false.js',['require','exports','module'],function (require, exports, module) {\n\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.default = false;\n\n});\n\n;define.alias('esm-env/node','esm-env/false.js');",
},
{
"path": "node_modules/loo/loo.mjs",
"contents": "define('loo/loo.mjs',['require','exports','module'],function (require, exports, module) {\n\n});\n\n;define.alias('loo','loo/loo.mjs');"
path: "node_modules/esm-env/index.js",
contents:
"define('esm-env/index.js',['require','exports','module','tslib','esm-env/browser','esm-env/development','esm-env/node'],function (require, exports, module) {\n\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.NODE = exports.DEV = exports.BROWSER = void 0;\nconst tslib_1 = require(\"tslib\");\nvar browser_1 = require(\"esm-env/browser\");\nObject.defineProperty(exports, \"BROWSER\", { enumerable: true, get: function () { return tslib_1.__importDefault(browser_1).default; } });\nvar development_1 = require(\"esm-env/development\");\nObject.defineProperty(exports, \"DEV\", { enumerable: true, get: function () { return tslib_1.__importDefault(development_1).default; } });\nvar node_1 = require(\"esm-env/node\");\nObject.defineProperty(exports, \"NODE\", { enumerable: true, get: function () { return tslib_1.__importDefault(node_1).default; } });\n\n});\n\n;define.alias('esm-env','esm-env/index.js');",
},
{
"contents": "define.switchToUserSpace();"
}
],
"appendFiles": [
{
"path": "local/after.js",
"contents": "after;"
path: "node_modules/esm-env/true.js",
contents:
"define('esm-env/true.js',['require','exports','module'],function (require, exports, module) {\n\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.default = true;\n\n});\n\n;define.alias('esm-env/browser','esm-env/true.js');",
},
{
"contents": "var ape = 1;"
}
{ contents: "define.switchToUserSpace();" },
],
"config": {
"baseUrl": "/dist",
"paths": {},
"bundles": {}
}
}
})
},
err => t.fail(err.stack)
);
config: { baseUrl: "/dist", paths: {}, bundles: {} },
},
});
});
});
Loading

0 comments on commit fc8f0fe

Please sign in to comment.