Skip to content

Commit

Permalink
fix(commonjs): proxy all entries to not break legacy polyfill plugins (
Browse files Browse the repository at this point in the history
  • Loading branch information
lukastaegert committed Apr 24, 2022
1 parent 1424249 commit 7434b0f
Show file tree
Hide file tree
Showing 23 changed files with 718 additions and 167 deletions.
2 changes: 1 addition & 1 deletion packages/commonjs/package.json
Expand Up @@ -52,7 +52,7 @@
"require"
],
"peerDependencies": {
"rollup": "^2.61.1"
"rollup": "^2.67.0"
},
"dependencies": {
"@rollup/pluginutils": "^3.1.0",
Expand Down
13 changes: 6 additions & 7 deletions packages/commonjs/src/generate-imports.js
Expand Up @@ -90,11 +90,12 @@ export function getRequireHandlers() {
exportsName,
id,
exportMode,
resolveRequireSourcesAndGetMeta,
resolveRequireSourcesAndUpdateMeta,
needsRequireWrapper,
isEsModule,
isDynamicRequireModulesEnabled,
getIgnoreTryCatchRequireStatementMode
getIgnoreTryCatchRequireStatementMode,
commonjsMeta
) {
const imports = [];
imports.push(`import * as ${helpersName} from "${HELPERS_ID}";`);
Expand All @@ -117,9 +118,10 @@ export function getRequireHandlers() {
);
}
const requiresBySource = collectSources(requireExpressions);
const { requireTargets, usesRequireWrapper } = await resolveRequireSourcesAndGetMeta(
const requireTargets = await resolveRequireSourcesAndUpdateMeta(
id,
needsRequireWrapper ? IS_WRAPPED_COMMONJS : !isEsModule,
commonjsMeta,
Object.keys(requiresBySource).map((source) => {
return {
source,
Expand All @@ -134,10 +136,7 @@ export function getRequireHandlers() {
getIgnoreTryCatchRequireStatementMode,
magicString
);
return {
importBlock: imports.length ? `${imports.join('\n')}\n\n` : '',
usesRequireWrapper
};
return imports.length ? `${imports.join('\n')}\n\n` : '';
}

return {
Expand Down
3 changes: 2 additions & 1 deletion packages/commonjs/src/helpers.js
Expand Up @@ -7,7 +7,8 @@ export const WRAPPED_SUFFIX = '?commonjs-wrapped';
export const EXTERNAL_SUFFIX = '?commonjs-external';
export const EXPORTS_SUFFIX = '?commonjs-exports';
export const MODULE_SUFFIX = '?commonjs-module';
export const ES_IMPORT_SUFFIX = '?es-import';
export const ENTRY_SUFFIX = '?commonjs-entry';
export const ES_IMPORT_SUFFIX = '?commonjs-es-import';

export const DYNAMIC_MODULES_ID = '\0commonjs-dynamic-modules';
export const HELPERS_ID = '\0commonjsHelpers.js';
Expand Down
69 changes: 42 additions & 27 deletions packages/commonjs/src/index.js
Expand Up @@ -9,6 +9,7 @@ import { getDynamicModuleRegistry, getDynamicRequireModules } from './dynamic-mo

import {
DYNAMIC_MODULES_ID,
ENTRY_SUFFIX,
ES_IMPORT_SUFFIX,
EXPORTS_SUFFIX,
EXTERNAL_SUFFIX,
Expand All @@ -20,13 +21,20 @@ import {
unwrapId
} from './helpers';
import { hasCjsKeywords } from './parse';
import { getEsImportProxy, getStaticRequireProxy, getUnknownRequireProxy } from './proxies';
import {
getEntryProxy,
getEsImportProxy,
getStaticRequireProxy,
getUnknownRequireProxy
} from './proxies';
import getResolveId from './resolve-id';
import { getResolveRequireSourcesAndGetMeta } from './resolve-require-sources';
import { getRequireResolver } from './resolve-require-sources';
import validateVersion from './rollup-version';
import transformCommonjs from './transform-commonjs';
import { getName, getStrictRequiresFilter, normalizePathSlashes } from './utils';

const PLUGIN_NAME = 'commonjs';

export default function commonjs(options = {}) {
const {
ignoreGlobal,
Expand Down Expand Up @@ -58,11 +66,6 @@ export default function commonjs(options = {}) {
: () =>
typeof defaultIsModuleExportsOption === 'boolean' ? defaultIsModuleExportsOption : 'auto';

const {
resolveRequireSourcesAndGetMeta,
getWrappedIds,
isRequiredId
} = getResolveRequireSourcesAndGetMeta(extensions, detectCyclesAndConditional);
const dynamicRequireRoot =
typeof options.dynamicRequireRoot === 'string'
? resolve(options.dynamicRequireRoot)
Expand All @@ -73,9 +76,6 @@ export default function commonjs(options = {}) {
);
const isDynamicRequireModulesEnabled = dynamicRequireModules.size > 0;

const esModulesWithDefaultExport = new Set();
const esModulesWithNamedExports = new Set();

const ignoreRequire =
typeof options.ignore === 'function'
? options.ignore
Expand Down Expand Up @@ -103,25 +103,31 @@ export default function commonjs(options = {}) {

const sourceMap = options.sourceMap !== false;

// Initialized in buildStart
let requireResolver;

function transformAndCheckExports(code, id) {
const { isEsModule, hasDefaultExport, hasNamedExports, ast } = analyzeTopLevelStatements(
this.parse,
code,
id
);

const commonjsMeta = this.getModuleInfo(id).meta.commonjs || {};
if (hasDefaultExport) {
esModulesWithDefaultExport.add(id);
commonjsMeta.hasDefaultExport = true;
}
if (hasNamedExports) {
esModulesWithNamedExports.add(id);
commonjsMeta.hasNamedExports = true;
}

if (
!dynamicRequireModules.has(normalizePathSlashes(id)) &&
(!(hasCjsKeywords(code, ignoreGlobal) || isRequiredId(id)) ||
(!(hasCjsKeywords(code, ignoreGlobal) || requireResolver.isRequiredId(id)) ||
(isEsModule && !options.transformMixedEsModules))
) {
return { meta: { commonjs: { isCommonJS: false } } };
commonjsMeta.isCommonJS = false;
return { meta: { commonjs: commonjsMeta } };
}

const needsRequireWrapper =
Expand Down Expand Up @@ -160,22 +166,23 @@ export default function commonjs(options = {}) {
ast,
getDefaultIsModuleExports(id),
needsRequireWrapper,
resolveRequireSourcesAndGetMeta(this),
isRequiredId(id),
checkDynamicRequire
requireResolver.resolveRequireSourcesAndUpdateMeta(this),
requireResolver.isRequiredId(id),
checkDynamicRequire,
commonjsMeta
);
}

return {
name: 'commonjs',
name: PLUGIN_NAME,

version,

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
? [rawOptions.plugins]
: [];
Expand All @@ -197,11 +204,12 @@ export default function commonjs(options = {}) {
'The namedExports option from "@rollup/plugin-commonjs" is deprecated. Named exports are now handled automatically.'
);
}
requireResolver = getRequireResolver(extensions, detectCyclesAndConditional);
},

buildEnd() {
if (options.strictRequires === 'debug') {
const wrappedIds = getWrappedIds();
const wrappedIds = requireResolver.getWrappedIds();
if (wrappedIds.length) {
this.warn({
code: 'WRAPPED_IDS',
Expand Down Expand Up @@ -250,6 +258,15 @@ export default function commonjs(options = {}) {
);
}

// entry suffix is just appended to not mess up relative external resolution
if (id.endsWith(ENTRY_SUFFIX)) {
return getEntryProxy(
id.slice(0, -ENTRY_SUFFIX.length),
defaultIsModuleExports,
this.getModuleInfo
);
}

if (isWrappedId(id, ES_IMPORT_SUFFIX)) {
return getEsImportProxy(unwrapId(id, ES_IMPORT_SUFFIX), defaultIsModuleExports);
}
Expand All @@ -265,18 +282,16 @@ export default function commonjs(options = {}) {

if (isWrappedId(id, PROXY_SUFFIX)) {
const actualId = unwrapId(id, PROXY_SUFFIX);
return getStaticRequireProxy(
actualId,
getRequireReturnsDefault(actualId),
esModulesWithDefaultExport,
esModulesWithNamedExports,
this.load
);
return getStaticRequireProxy(actualId, getRequireReturnsDefault(actualId), this.load);
}

return null;
},

shouldTransformCachedModule(...args) {
return requireResolver.shouldTransformCachedModule.call(this, ...args);
},

transform(code, id) {
const extName = extname(id);
if (extName !== '.cjs' && (!filter(id) || !extensions.includes(extName))) {
Expand Down
36 changes: 23 additions & 13 deletions packages/commonjs/src/proxies.js
@@ -1,4 +1,4 @@
import { HELPERS_ID } from './helpers';
import { HELPERS_ID, IS_WRAPPED_COMMONJS } from './helpers';
import { capitalize, getName } from './utils';

export function getUnknownRequireProxy(id, requireReturnsDefault) {
Expand All @@ -17,36 +17,46 @@ export function getUnknownRequireProxy(id, requireReturnsDefault) {
return `import * as ${name} from ${JSON.stringify(id)}; ${exported}`;
}

export async function getStaticRequireProxy(
id,
requireReturnsDefault,
esModulesWithDefaultExport,
esModulesWithNamedExports,
loadModule
) {
export async function getStaticRequireProxy(id, requireReturnsDefault, loadModule) {
const name = getName(id);
const {
meta: { commonjs: commonjsMeta }
} = await loadModule({ id });
if (commonjsMeta && commonjsMeta.isCommonJS) {
return `export { __moduleExports as default } from ${JSON.stringify(id)};`;
} else if (!commonjsMeta) {
if (!commonjsMeta) {
return getUnknownRequireProxy(id, requireReturnsDefault);
} else if (commonjsMeta.isCommonJS) {
return `export { __moduleExports as default } from ${JSON.stringify(id)};`;
} else if (!requireReturnsDefault) {
return `import { getAugmentedNamespace } from "${HELPERS_ID}"; import * as ${name} from ${JSON.stringify(
id
)}; export default /*@__PURE__*/getAugmentedNamespace(${name});`;
} else if (
requireReturnsDefault !== true &&
(requireReturnsDefault === 'namespace' ||
!esModulesWithDefaultExport.has(id) ||
(requireReturnsDefault === 'auto' && esModulesWithNamedExports.has(id)))
!commonjsMeta.hasDefaultExport ||
(requireReturnsDefault === 'auto' && commonjsMeta.hasNamedExports))
) {
return `import * as ${name} from ${JSON.stringify(id)}; export default ${name};`;
}
return `export { default } from ${JSON.stringify(id)};`;
}

export function getEntryProxy(id, defaultIsModuleExports, getModuleInfo) {
const {
meta: { commonjs: commonjsMeta },
hasDefaultExport
} = getModuleInfo(id);
if (!commonjsMeta || commonjsMeta.isCommonJS !== IS_WRAPPED_COMMONJS) {
const stringifiedId = JSON.stringify(id);
let code = `export * from ${stringifiedId};`;
if (hasDefaultExport) {
code += `export { default } from ${stringifiedId};`;
}
return code;
}
return getEsImportProxy(id, defaultIsModuleExports);
}

export function getEsImportProxy(id, defaultIsModuleExports) {
const name = getName(id);
const exportsName = `${name}Exports`;
Expand Down
35 changes: 20 additions & 15 deletions packages/commonjs/src/resolve-id.js
Expand Up @@ -5,6 +5,7 @@ import { dirname, resolve, sep } from 'path';

import {
DYNAMIC_MODULES_ID,
ENTRY_SUFFIX,
ES_IMPORT_SUFFIX,
EXPORTS_SUFFIX,
EXTERNAL_SUFFIX,
Expand Down Expand Up @@ -51,18 +52,16 @@ export function resolveExtensions(importee, importer, extensions) {
export default function getResolveId(extensions) {
return async function resolveId(importee, importer, resolveOptions) {
// We assume that all requires are pre-resolved
if (
resolveOptions.custom &&
resolveOptions.custom['node-resolve'] &&
resolveOptions.custom['node-resolve'].isRequire
) {
const customOptions = resolveOptions.custom;
if (customOptions && customOptions['node-resolve'] && customOptions['node-resolve'].isRequire) {
return null;
}
if (isWrappedId(importee, WRAPPED_SUFFIX)) {
return unwrapId(importee, WRAPPED_SUFFIX);
}

if (
importee.endsWith(ENTRY_SUFFIX) ||
isWrappedId(importee, MODULE_SUFFIX) ||
isWrappedId(importee, EXPORTS_SUFFIX) ||
isWrappedId(importee, PROXY_SUFFIX) ||
Expand All @@ -79,7 +78,8 @@ export default function getResolveId(extensions) {
importer === DYNAMIC_MODULES_ID ||
// Proxies are only importing resolved ids, no need to resolve again
isWrappedId(importer, PROXY_SUFFIX) ||
isWrappedId(importer, ES_IMPORT_SUFFIX)
isWrappedId(importer, ES_IMPORT_SUFFIX) ||
importer.endsWith(ENTRY_SUFFIX)
) {
return importee;
}
Expand All @@ -99,22 +99,27 @@ export default function getResolveId(extensions) {

// If this is an entry point or ESM import, we need to figure out if the importee is wrapped and
// if that is the case, we need to add a proxy.
const customOptions = resolveOptions.custom;

// If this is a require, we do not need a proxy
if (customOptions && customOptions['node-resolve'] && customOptions['node-resolve'].isRequire) {
return null;
}

const resolved =
(await this.resolve(importee, importer, Object.assign({ skipSelf: true }, resolveOptions))) ||
resolveExtensions(importee, importer, extensions);
if (!resolved || resolved.external) {
// Make sure that even if other plugins resolve again, we ignore our own proxies
if (
!resolved ||
resolved.external ||
resolved.id.endsWith(ENTRY_SUFFIX) ||
isWrappedId(resolved.id, ES_IMPORT_SUFFIX)
) {
return resolved;
}
const moduleInfo = await this.load(resolved);
if (resolveOptions.isEntry) {
moduleInfo.moduleSideEffects = true;
// We must not precede entry proxies with a `\0` as that will mess up relative external resolution
return resolved.id + ENTRY_SUFFIX;
}
const {
meta: { commonjs: commonjsMeta }
} = await this.load(resolved);
} = moduleInfo;
if (commonjsMeta && commonjsMeta.isCommonJS === IS_WRAPPED_COMMONJS) {
return wrapId(resolved.id, ES_IMPORT_SUFFIX);
}
Expand Down

0 comments on commit 7434b0f

Please sign in to comment.