Skip to content

Commit

Permalink
feat(commonjs): make namespace callable when requiring ESM with funct…
Browse files Browse the repository at this point in the history
…ion default (#1038)
  • Loading branch information
lukastaegert committed Apr 24, 2022
1 parent 2aa0ac9 commit 3c00191
Show file tree
Hide file tree
Showing 34 changed files with 604 additions and 278 deletions.
4 changes: 3 additions & 1 deletion packages/commonjs/README.md
Expand Up @@ -42,6 +42,8 @@ export default {

Then call `rollup` either via the [CLI](https://www.rollupjs.org/guide/en/#command-line-reference) or the [API](https://www.rollupjs.org/guide/en/#javascript-api).

When used together with the node-resolve plugin

## Options

### `strictRequires`
Expand Down Expand Up @@ -378,7 +380,7 @@ export default {
format: 'iife',
name: 'MyModule'
},
plugins: [resolve(), commonjs()]
plugins: [commonjs(), resolve()]
};
```

Expand Down
2 changes: 1 addition & 1 deletion packages/commonjs/package.json
Expand Up @@ -60,7 +60,7 @@
},
"devDependencies": {
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^8.4.0",
"@rollup/plugin-node-resolve": "^13.0.6",
"locate-character": "^2.0.5",
"require-relative": "^0.8.7",
"rollup": "^2.67.3",
Expand Down
62 changes: 57 additions & 5 deletions packages/commonjs/src/dynamic-modules.js
@@ -1,10 +1,63 @@
import { existsSync, readFileSync, statSync } from 'fs';
import { join, resolve } from 'path';

import glob from 'glob';

import { getVirtualPathForDynamicRequirePath, normalizePathSlashes } from './utils';

function getPackageEntryPoint(dirPath) {
let entryPoint = 'index.js';

try {
if (existsSync(join(dirPath, 'package.json'))) {
entryPoint =
JSON.parse(readFileSync(join(dirPath, 'package.json'), { encoding: 'utf8' })).main ||
entryPoint;
}
} catch (ignored) {
// ignored
}

return entryPoint;
}

function isDirectory(path) {
try {
if (statSync(path).isDirectory()) return true;
} catch (ignored) {
// Nothing to do here
}
return false;
}

export function getDynamicRequireModules(patterns) {
const dynamicRequireModules = new Map();
for (const pattern of !patterns || Array.isArray(patterns) ? patterns || [] : [patterns]) {
const isNegated = pattern.startsWith('!');
const modifyMap = (targetPath, resolvedPath) =>
isNegated
? dynamicRequireModules.delete(targetPath)
: dynamicRequireModules.set(targetPath, resolvedPath);
for (const path of glob.sync(isNegated ? pattern.substr(1) : pattern)) {
const resolvedPath = resolve(path);
const requirePath = normalizePathSlashes(resolvedPath);
if (isDirectory(resolvedPath)) {
const modulePath = resolve(join(resolvedPath, getPackageEntryPoint(path)));
modifyMap(requirePath, modulePath);
modifyMap(normalizePathSlashes(modulePath), modulePath);
} else {
modifyMap(requirePath, resolvedPath);
}
}
}
return dynamicRequireModules;
}

const FAILED_REQUIRE_ERROR = `throw new Error('Could not dynamically require "' + path + '". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.');`;

export function getDynamicRequireModules(
export function getDynamicModuleRegistry(
isDynamicRequireModulesEnabled,
dynamicRequireModuleSet,
dynamicRequireModules,
commonDir,
ignoreDynamicRequires
) {
Expand All @@ -13,16 +66,15 @@ export function getDynamicRequireModules(
${FAILED_REQUIRE_ERROR}
}`;
}
const dynamicModuleIds = [...dynamicRequireModuleSet];
const dynamicModuleImports = dynamicModuleIds
const dynamicModuleImports = [...dynamicRequireModules.values()]
.map(
(id, index) =>
`import ${
id.endsWith('.json') ? `json${index}` : `{ __require as require${index} }`
} from ${JSON.stringify(id)};`
)
.join('\n');
const dynamicModuleProps = dynamicModuleIds
const dynamicModuleProps = [...dynamicRequireModules.keys()]
.map(
(id, index) =>
`\t\t${JSON.stringify(
Expand Down
46 changes: 0 additions & 46 deletions packages/commonjs/src/dynamic-require-paths.js

This file was deleted.

7 changes: 4 additions & 3 deletions packages/commonjs/src/generate-imports.js
Expand Up @@ -68,11 +68,11 @@ export function getRequireStringArg(node) {
: node.arguments[0].quasis[0].value.cooked;
}

export function hasDynamicModuleForPath(source, id, dynamicRequireModuleSet) {
export function hasDynamicModuleForPath(source, id, dynamicRequireModules) {
if (!/^(?:\.{0,2}[/\\]|[A-Za-z]:[/\\])/.test(source)) {
try {
const resolvedPath = normalizePathSlashes(nodeResolveSync(source, { basedir: dirname(id) }));
if (dynamicRequireModuleSet.has(resolvedPath)) {
if (dynamicRequireModules.has(resolvedPath)) {
return true;
}
} catch (ex) {
Expand All @@ -85,7 +85,7 @@ export function hasDynamicModuleForPath(source, id, dynamicRequireModuleSet) {

for (const attemptExt of ['', '.js', '.json']) {
const resolvedPath = normalizePathSlashes(resolve(dirname(id), source + attemptExt));
if (dynamicRequireModuleSet.has(resolvedPath)) {
if (dynamicRequireModules.has(resolvedPath)) {
return true;
}
}
Expand Down Expand Up @@ -119,6 +119,7 @@ export function getRequireHandlers() {
const imports = [];
imports.push(`import * as ${helpersName} from "${HELPERS_ID}";`);
if (usesRequire) {
// TODO Lukas check where to import it from or change to usesDynamicRequire
imports.push(
`import { commonjsRequire as ${dynamicRequireName} } from "${DYNAMIC_MODULES_ID}";`
);
Expand Down
11 changes: 9 additions & 2 deletions packages/commonjs/src/helpers.js
Expand Up @@ -3,6 +3,7 @@ export const wrapId = (id, suffix) => `\0${id}${suffix}`;
export const unwrapId = (wrappedId, suffix) => wrappedId.slice(1, -suffix.length);

export const PROXY_SUFFIX = '?commonjs-proxy';
export const WRAPPED_SUFFIX = '?commonjs-wrapped';
export const EXTERNAL_SUFFIX = '?commonjs-external';
export const EXPORTS_SUFFIX = '?commonjs-exports';
export const MODULE_SUFFIX = '?commonjs-module';
Expand Down Expand Up @@ -33,8 +34,14 @@ export function getDefaultExportFromNamespaceIfNotNamed (n) {
}
export function getAugmentedNamespace(n) {
if (n.__esModule) return n;
var a = Object.defineProperty({}, '__esModule', {value: true});
var f = n.default;
if (typeof f == "function") {
var a = function () {
return f.apply(this, arguments);
};
a.prototype = f.prototype;
} else a = {};
Object.defineProperty(a, '__esModule', {value: true});
Object.keys(n).forEach(function (k) {
var d = Object.getOwnPropertyDescriptor(n, k);
Object.defineProperty(a, k, d.get ? d : {
Expand Down
48 changes: 23 additions & 25 deletions packages/commonjs/src/index.js
Expand Up @@ -6,9 +6,8 @@ import getCommonDir from 'commondir';
import { peerDependencies } from '../package.json';

import analyzeTopLevelStatements from './analyze-top-level-statements';
import { getDynamicRequireModules } from './dynamic-modules';
import { getDynamicModuleRegistry, getDynamicRequireModules } from './dynamic-modules';

import getDynamicRequireModuleSet from './dynamic-require-paths';
import {
DYNAMIC_MODULES_ID,
ES_IMPORT_SUFFIX,
Expand Down Expand Up @@ -65,10 +64,11 @@ export default function commonjs(options = {}) {
getWrappedIds,
isRequiredId
} = getResolveRequireSourcesAndGetMeta(extensions, detectCycles);
const dynamicRequireModuleSet = getDynamicRequireModuleSet(options.dynamicRequireTargets);
const isDynamicRequireModulesEnabled = dynamicRequireModuleSet.size > 0;
const dynamicRequireModules = getDynamicRequireModules(options.dynamicRequireTargets);
const isDynamicRequireModulesEnabled = dynamicRequireModules.size > 0;
// TODO Lukas do we need the CWD?
const commonDir = isDynamicRequireModulesEnabled
? getCommonDir(null, Array.from(dynamicRequireModuleSet).concat(process.cwd()))
? getCommonDir(null, Array.from(dynamicRequireModules.keys()).concat(process.cwd()))
: null;

const esModulesWithDefaultExport = new Set();
Expand Down Expand Up @@ -115,7 +115,7 @@ export default function commonjs(options = {}) {
}

if (
!dynamicRequireModuleSet.has(normalizePathSlashes(id)) &&
!dynamicRequireModules.has(normalizePathSlashes(id)) &&
(!(hasCjsKeywords(code, ignoreGlobal) || isRequiredId(id)) ||
(isEsModule && !options.transformMixedEsModules))
) {
Expand All @@ -124,7 +124,7 @@ export default function commonjs(options = {}) {

const needsRequireWrapper =
!isEsModule &&
(dynamicRequireModuleSet.has(normalizePathSlashes(id)) || strictRequiresFilter(id));
(dynamicRequireModules.has(normalizePathSlashes(id)) || strictRequiresFilter(id));

return transformCommonjs(
this.parse,
Expand All @@ -137,7 +137,7 @@ export default function commonjs(options = {}) {
getIgnoreTryCatchRequireStatementMode,
sourceMap,
isDynamicRequireModulesEnabled,
dynamicRequireModuleSet,
dynamicRequireModules,
commonDir,
ast,
getDefaultIsModuleExports(id),
Expand All @@ -150,18 +150,19 @@ export default function commonjs(options = {}) {
return {
name: 'commonjs',

options(options) {
// Always sort the node-resolve plugin after the commonjs plugin as otherwise CommonJS entries
// will not work with strictRequires: true
const { plugins } = options;
if (Array.isArray(plugins)) {
const cjsIndex = plugins.findIndex((plugin) => plugin.name === 'commonjs');
const nodeResolveIndex = plugins.findIndex((plugin) => plugin.name === 'node-resolve');
if (nodeResolveIndex >= 0 && nodeResolveIndex < cjsIndex) {
plugins.splice(cjsIndex + 1, 0, plugins[nodeResolveIndex]);
plugins.splice(nodeResolveIndex, 1);
}
}
options(rawOptions) {
// We inject the resolver in the beginning so that "catch-all-resolver" like node-resolver
// do not prevent our plugin from resolving entry points ot proxies.
const plugins = Array.isArray(rawOptions.plugins)
? rawOptions.plugins
: rawOptions.plugins
? [rawOptions.plugins]
: [];
plugins.unshift({
name: 'commonjs--resolver',
resolveId
});
return { ...rawOptions, plugins };
},

buildStart() {
Expand All @@ -185,7 +186,6 @@ export default function commonjs(options = {}) {
.join(',\n')}\n]`
});
} else {
// TODO Lukas test
this.warn({
code: 'WRAPPED_IDS',
ids: wrappedIds,
Expand All @@ -195,8 +195,6 @@ export default function commonjs(options = {}) {
}
},

resolveId,

load(id) {
if (id === HELPERS_ID) {
return getHelpersModule();
Expand Down Expand Up @@ -232,9 +230,9 @@ export default function commonjs(options = {}) {
}

if (id === DYNAMIC_MODULES_ID) {
return getDynamicRequireModules(
return getDynamicModuleRegistry(
isDynamicRequireModulesEnabled,
dynamicRequireModuleSet,
dynamicRequireModules,
commonDir,
ignoreDynamicRequires
);
Expand Down

0 comments on commit 3c00191

Please sign in to comment.