Skip to content

Commit

Permalink
feat(commonjs): reimplement dynamic import handling (requires Node 12…
Browse files Browse the repository at this point in the history
…, no longer supports require.cache) (#1038)

BREAKING CHANGES: requires Node 12
No longer supports require.cache
  • Loading branch information
lukastaegert committed Apr 24, 2022
1 parent a6f8077 commit 493f753
Show file tree
Hide file tree
Showing 80 changed files with 2,508 additions and 4,268 deletions.
2 changes: 1 addition & 1 deletion packages/commonjs/package.json
Expand Up @@ -16,7 +16,7 @@
"main": "dist/index.js",
"module": "dist/index.es.js",
"engines": {
"node": ">= 8.0.0"
"node": ">= 12.0.0"
},
"scripts": {
"build": "rollup -c",
Expand Down
144 changes: 144 additions & 0 deletions packages/commonjs/src/dynamic-modules.js
@@ -0,0 +1,144 @@
import { getVirtualPathForDynamicRequirePath, normalizePathSlashes } from './utils';

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(
isDynamicRequireModulesEnabled,
dynamicRequireModuleSet,
commonDir,
ignoreDynamicRequires
) {
if (!isDynamicRequireModulesEnabled) {
return `export function commonjsRequire(path) {
${FAILED_REQUIRE_ERROR}
}`;
}
const dynamicModuleIds = [...dynamicRequireModuleSet];
const dynamicModuleImports = dynamicModuleIds
.map(
(id, index) =>
`import ${
id.endsWith('.json') ? `json${index}` : `{ __require as require${index} }`
} from ${JSON.stringify(id)};`
)
.join('\n');
const dynamicModuleProps = dynamicModuleIds
.map(
(id, index) =>
`\t\t${JSON.stringify(
getVirtualPathForDynamicRequirePath(normalizePathSlashes(id), commonDir)
)}: ${id.endsWith('.json') ? `function () { return json${index}; }` : `require${index}`}`
)
.join(',\n');
return `${dynamicModuleImports}
var dynamicModules;
function getDynamicModules() {
return dynamicModules || (dynamicModules = {
${dynamicModuleProps}
});
}
export function commonjsRequire(path, originalModuleDir) {
var resolvedPath = commonjsResolveImpl(path, originalModuleDir, true);
if (resolvedPath !== null) {
return getDynamicModules()[resolvedPath]();
}
${ignoreDynamicRequires ? 'return require(path);' : FAILED_REQUIRE_ERROR}
}
function commonjsResolve (path, originalModuleDir) {
const resolvedPath = commonjsResolveImpl(path, originalModuleDir);
if (resolvedPath !== null) {
return resolvedPath;
}
return require.resolve(path);
}
commonjsRequire.resolve = commonjsResolve;
function commonjsResolveImpl (path, originalModuleDir) {
var shouldTryNodeModules = isPossibleNodeModulesPath(path);
path = normalize(path);
var relPath;
if (path[0] === '/') {
originalModuleDir = '/';
}
var modules = getDynamicModules();
var checkedExtensions = ['', '.js', '.json'];
while (true) {
if (!shouldTryNodeModules) {
relPath = originalModuleDir ? normalize(originalModuleDir + '/' + path) : path;
} else if (originalModuleDir) {
relPath = normalize(originalModuleDir + '/node_modules/' + path);
} else {
relPath = normalize(join('node_modules', path));
}
if (relPath.endsWith('/..')) {
break; // Travelled too far up, avoid infinite loop
}
for (var extensionIndex = 0; extensionIndex < checkedExtensions.length; extensionIndex++) {
var resolvedPath = relPath + checkedExtensions[extensionIndex];
if (modules[resolvedPath]) {
return resolvedPath;
}
}
if (!shouldTryNodeModules) break;
var nextDir = normalize(originalModuleDir + '/..');
if (nextDir === originalModuleDir) break;
originalModuleDir = nextDir;
}
return null;
}
function isPossibleNodeModulesPath (modulePath) {
var c0 = modulePath[0];
if (c0 === '/' || c0 === '\\\\') return false;
var c1 = modulePath[1], c2 = modulePath[2];
if ((c0 === '.' && (!c1 || c1 === '/' || c1 === '\\\\')) ||
(c0 === '.' && c1 === '.' && (!c2 || c2 === '/' || c2 === '\\\\'))) return false;
if (c1 === ':' && (c2 === '/' || c2 === '\\\\')) return false;
return true;
}
function normalize (path) {
path = path.replace(/\\\\/g, '/');
var parts = path.split('/');
var slashed = parts[0] === '';
for (var i = 1; i < parts.length; i++) {
if (parts[i] === '.' || parts[i] === '') {
parts.splice(i--, 1);
}
}
for (var i = 1; i < parts.length; i++) {
if (parts[i] !== '..') continue;
if (i > 0 && parts[i - 1] !== '..' && parts[i - 1] !== '.') {
parts.splice(--i, 2);
i--;
}
}
path = parts.join('/');
if (slashed && path[0] !== '/') path = '/' + path;
else if (path.length === 0) path = '.';
return path;
}
function join () {
if (arguments.length === 0) return '.';
var joined;
for (var i = 0; i < arguments.length; ++i) {
var arg = arguments[i];
if (arg.length > 0) {
if (joined === undefined)
joined = arg;
else
joined += '/' + arg;
}
}
if (joined === undefined) return '.';
return joined;
}`;
}
56 changes: 0 additions & 56 deletions packages/commonjs/src/dynamic-packages-manager.js

This file was deleted.

27 changes: 19 additions & 8 deletions packages/commonjs/src/dynamic-require-paths.js
@@ -1,11 +1,25 @@
import { statSync } from 'fs';

import { existsSync, readFileSync, statSync } from 'fs';
import { join, resolve } from 'path';

import glob from 'glob';

import { normalizePathSlashes } from './utils';
import { getPackageEntryPoint } from './dynamic-packages-manager';

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 {
Expand All @@ -16,7 +30,7 @@ function isDirectory(path) {
return false;
}

export default function getDynamicRequirePaths(patterns) {
export default function getDynamicRequireModuleSet(patterns) {
const dynamicRequireModuleSet = new Set();
for (const pattern of !patterns || Array.isArray(patterns) ? patterns || [] : [patterns]) {
const isNegated = pattern.startsWith('!');
Expand All @@ -28,8 +42,5 @@ export default function getDynamicRequirePaths(patterns) {
}
}
}
const dynamicRequireModuleDirPaths = Array.from(dynamicRequireModuleSet.values()).filter((path) =>
isDirectory(path)
);
return { dynamicRequireModuleSet, dynamicRequireModuleDirPaths };
return dynamicRequireModuleSet;
}
38 changes: 35 additions & 3 deletions packages/commonjs/src/generate-exports.js
Expand Up @@ -30,12 +30,43 @@ export function rewriteExportsAndGetExportsBlock(
HELPERS_NAME,
exportMode,
detectWrappedDefault,
defaultIsModuleExports
defaultIsModuleExports,
usesRequireWrapper,
requireName
) {
const exports = [];
const exportDeclarations = [];

if (exportMode === 'replace') {
if (usesRequireWrapper) {
// TODO Lukas Extract
if (exportMode === 'replace') {
for (const { left } of moduleExportsAssignments) {
magicString.overwrite(left.start, left.end, exportsName);
}
} else {
// Collect and rewrite module.exports assignments
for (const { left } of moduleExportsAssignments) {
magicString.overwrite(left.start, left.end, `${moduleName}.exports`);
}
// Collect and rewrite named exports
for (const [exportName, { nodes }] of exportsAssignmentsByName) {
for (const node of nodes) {
magicString.overwrite(node.start, node.left.end, `${exportsName}.${exportName}`);
}
}
// Collect and rewrite exports.__esModule assignments
for (const expression of defineCompiledEsmExpressions) {
const moduleExportsExpression =
expression.type === 'CallExpression' ? expression.arguments[0] : expression.left.object;
magicString.overwrite(
moduleExportsExpression.start,
moduleExportsExpression.end,
exportsName
);
}
}
exports.push(`${requireName} as __require`);
} else if (exportMode === 'replace') {
getExportsForReplacedModuleExports(
magicString,
exports,
Expand Down Expand Up @@ -165,7 +196,8 @@ function getExports(
}

if (!isRestorableCompiledEsm || defaultIsModuleExports === true) {
exportDeclarations.push(`export default ${exportsName};`);
// TODO Lukas handle ESM importing CommonJS
exports.push(`${exportsName} as default`);
} else if (moduleExportsAssignments.length === 0 || defaultIsModuleExports === false) {
exports.push(`${deconflictedDefaultExportName || exportsName} as default`);
} else {
Expand Down

0 comments on commit 493f753

Please sign in to comment.