Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exported "namespaced imports" are lost when treeshake is false #4174

Closed
milesj opened this issue Jul 9, 2021 · 4 comments · Fixed by #4175
Closed

Exported "namespaced imports" are lost when treeshake is false #4174

milesj opened this issue Jul 9, 2021 · 4 comments · Fixed by #4175

Comments

@milesj
Copy link

milesj commented Jul 9, 2021

Rollup Version

2.53.0

Operating System (or Browser)

Linux, macOS

Node Version (if applicable)

10+

Link To Reproduction

https://github.com/milesj/rollup-preserve-namespaces

PR I encountered it: milesj/boost#151

Expected Behaviour

It looks like namespace imports that are not used but are exported are dropped, for example this fails:

export * as ns from './namespace';

However, namespace imports that are used within the same file that are exported persist, for example this passes:

import * as ns from './namespace';

export { ns };
export const test = { ...ns };

Discovery

I started updating a project of mine to the latest Rollup version, and immediately noticed that a ton of unit tests are failing with the following errors (as seen in the REPL/PR link):

TypeError: Cannot read property 'parse' of undefined

      14 |
      15 |       resp.on('end', () => {
    > 16 |         const pkg = json.parse<{ 'dist-tags': Record<string, string> }>(data);
TypeError: Cannot read property 'console' of undefined

      114 |       transports: [
      115 |         new StreamTransport({
    > 116 |           format: formats.console,

This is very weird, as this code hasn't changed in a long time and is pretty straight forward. After digging into it further, it looks like namespaced imports that are exported are completely dropped and are no longer exported in the transpiled output. For example, this code:

/**
 * @copyright   2020, Miles Johnson
 * @license     https://opensource.org/licenses/MIT
 */

import optimal, { Blueprint, Predicates, predicates } from 'optimal';
import type { CommonErrorCode } from './CommonError';
import CommonError from './CommonError';
import Contract from './Contract';
import ExitError from './ExitError';
import PackageGraph from './PackageGraph';
import Path from './Path';
import PathResolver from './PathResolver';
import Project from './Project';

export * from './constants';
export * from './helpers';
export * from './types';
export * from '@boost/decorators';

// THESE 2 EXPORTS
export * as json from './serializers/json';
export * as yaml from './serializers/yaml';

export {
  CommonError,
  Contract,
  ExitError,
  optimal,
  PackageGraph,
  Path,
  PathResolver,
  predicates,
  Project,
};

export type { Blueprint, CommonErrorCode, Predicates };

Is transpiled to the following. Notice that json and yaml exports do not exist! The files are required, but they do nothing.

'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

var optimal = require('optimal');

var CommonError = require('./CommonError.js');

var Contract = require('./Contract.js');

var ExitError = require('./ExitError.js');

var PackageGraph = require('./PackageGraph.js');

var Path = require('./Path.js');

var PathResolver = require('./PathResolver.js');

var Project = require('./Project.js');

var constants = require('./constants.js');

require('./helpers/index.js');

require('./serializers/json.js');

require('./serializers/yaml.js');

var types = require('./types.js');

var decorators = require('@boost/decorators');

var createBlueprint = require('./helpers/createBlueprint.js');

var deepFreeze = require('./helpers/deepFreeze.js');

var deepMerge = require('./helpers/deepMerge.js');

var formatMs = require('./helpers/formatMs.js');

var instanceOf = require('./helpers/instanceOf.js');

var isEmpty = require('./helpers/isEmpty.js');

var isFilePath = require('./helpers/isFilePath.js');

var isModuleName = require('./helpers/isModuleName.js');

var isObject = require('./helpers/isObject.js');

var isPlainObject = require('./helpers/isPlainObject.js');

var parseFile = require('./helpers/parseFile.js');

var requireModule = require('./helpers/requireModule.js');

var requireTypedModule = require('./helpers/requireTypedModule.js');

var toArray = require('./helpers/toArray.js');

function _interopDefaultLegacy(e) {
  return e && typeof e === 'object' && 'default' in e ? e : {
    'default': e
  };
}

var optimal__default = /*#__PURE__*/_interopDefaultLegacy(optimal);
/**
 * @copyright   2020, Miles Johnson
 * @license     https://opensource.org/licenses/MIT
 */


Object.defineProperty(exports, 'optimal', {
  enumerable: true,
  get: function () {
    return optimal__default['default'];
  }
});
Object.defineProperty(exports, 'predicates', {
  enumerable: true,
  get: function () {
    return optimal.predicates;
  }
});
exports.CommonError = CommonError;
exports.Contract = Contract;
exports.ExitError = ExitError;
exports.PackageGraph = PackageGraph;
exports.Path = Path;
exports.PathResolver = PathResolver;
exports.Project = Project;
exports.MODULE_NAME_PART = constants.MODULE_NAME_PART;
exports.MODULE_NAME_PATTERN = constants.MODULE_NAME_PATTERN;
Object.defineProperty(exports, 'LookupType', {
  enumerable: true,
  get: function () {
    return types.LookupType;
  }
});
exports.createBlueprint = createBlueprint;
exports.deepFreeze = deepFreeze;
exports.deepMerge = deepMerge;
exports.formatMs = formatMs;
exports.instanceOf = instanceOf;
exports.isEmpty = isEmpty;
exports.isFilePath = isFilePath;
exports.isModuleName = isModuleName;
exports.isObject = isObject;
exports.isPlainObject = isPlainObject;
exports.parseFile = parseFile;
exports.requireModule = requireModule;
exports.requireTypedModule = requireTypedModule;
exports.toArray = toArray;
Object.keys(decorators).forEach(function (k) {
  if (k !== 'default' && !exports.hasOwnProperty(k)) Object.defineProperty(exports, k, {
    enumerable: true,
    get: function () {
      return decorators[k];
    }
  });
});
//# sourceMappingURL=index.js.map

As for the other error above, it is also a namespaced import being exported (https://github.com/milesj/boost/blob/master/packages/log/src/index.ts#L7)

import * as formats from './formats';

export { formats };

Attempts

I've tried different combinations of interop, exports, treeshake, sourceMaps, and any option I could think of. None of them seemed to resolve this issue.

HOWEVER, when I set preserveModules to false and use a single entry point, the export DOES correctly exist.

Instead of export * as X, I also tried the following. It has the same problem (https://github.com/milesj/boost/blob/master/packages/common/src/index.ts#L15).

import * as json from './serializers/json';
import * as yaml from './serializers/yaml';

export { json, yaml };

Config

The configuration is generated by Packemon, which can be found here: https://github.com/milesj/packemon/blob/master/src/rollup/config.ts

Since it's kind of abstracted away, here's a console log of the config while running the build.

{
  cache: undefined,
  external: [Function (anonymous)],
  input: [
    '/Users/milesj/Projects/boost/packages/common/src/CommonError.ts',
    '/Users/milesj/Projects/boost/packages/common/src/Contract.ts',
    '/Users/milesj/Projects/boost/packages/common/src/ExitError.ts',
    '/Users/milesj/Projects/boost/packages/common/src/PackageGraph.ts',
    '/Users/milesj/Projects/boost/packages/common/src/Path.ts',
    '/Users/milesj/Projects/boost/packages/common/src/PathResolver.ts',
    '/Users/milesj/Projects/boost/packages/common/src/Project.ts',
    '/Users/milesj/Projects/boost/packages/common/src/constants.ts',
    '/Users/milesj/Projects/boost/packages/common/src/index.ts',
    '/Users/milesj/Projects/boost/packages/common/src/types.ts',
    '/Users/milesj/Projects/boost/packages/common/src/helpers/createBlueprint.ts',
    '/Users/milesj/Projects/boost/packages/common/src/helpers/deepFreeze.ts',
    '/Users/milesj/Projects/boost/packages/common/src/helpers/deepMerge.ts',
    '/Users/milesj/Projects/boost/packages/common/src/helpers/formatMs.ts',
    '/Users/milesj/Projects/boost/packages/common/src/helpers/index.ts',
    '/Users/milesj/Projects/boost/packages/common/src/helpers/instanceOf.ts',
    '/Users/milesj/Projects/boost/packages/common/src/helpers/isEmpty.ts',
    '/Users/milesj/Projects/boost/packages/common/src/helpers/isFilePath.ts',
    '/Users/milesj/Projects/boost/packages/common/src/helpers/isModuleName.ts',
    '/Users/milesj/Projects/boost/packages/common/src/helpers/isObject.ts',
    '/Users/milesj/Projects/boost/packages/common/src/helpers/isPlainObject.ts',
    '/Users/milesj/Projects/boost/packages/common/src/helpers/parseFile.ts',
    '/Users/milesj/Projects/boost/packages/common/src/helpers/requireModule.ts',
    '/Users/milesj/Projects/boost/packages/common/src/helpers/requireTypedModule.ts',
    '/Users/milesj/Projects/boost/packages/common/src/helpers/toArray.ts',
    '/Users/milesj/Projects/boost/packages/common/src/internal/interopRequireModule.ts',
    '/Users/milesj/Projects/boost/packages/common/src/serializers/json.ts',
    '/Users/milesj/Projects/boost/packages/common/src/serializers/yaml.ts'
  ],
  output: [
    {
      dir: '/Users/milesj/Projects/boost/packages/common/lib',
      format: 'cjs',
      originalFormat: 'lib',
      paths: {},
      assetFileNames: '../assets/[name]-[hash][extname]',
      chunkFileNames: '[name]-[hash].js',
      entryFileNames: '[name].js',
      preserveModules: true,
      preferConst: false,
      plugins: [
        {
          name: 'babel',
          renderStart: [Function: renderStart],
          renderChunk: [Function: renderChunk]
        },
        {
          name: 'packemon-add-bin-shebang',
          generateBundle: [Function: generateBundle]
        }
      ],
      sourcemap: true,
      sourcemapExcludeSources: true,
      exports: 'auto'
    }
  ],
  plugins: [
    {
      name: 'node-externals',
      resolveId: [Function: resolveId],
      buildStart: [Function: buildStart]
    },
    {
      name: 'node-resolve',
      buildStart: [Function: buildStart],
      generateBundle: [Function: generateBundle],
      resolveId: [AsyncFunction: resolveId],
      load: [Function: load],
      getPackageInfoForId: [Function: getPackageInfoForId]
    },
    {
      name: 'commonjs',
      buildStart: [Function: buildStart],
      resolveId: [Function: resolveId],
      load: [Function: load],
      transform: [Function: transform],
      moduleParsed: [Function: moduleParsed]
    },
    { name: 'json', transform: [Function: transform] },
    {
      name: 'babel',
      options: [Function: options],
      resolveId: [Function: resolveId],
      load: [Function: load],
      transform: [Function: transform]
    }
  ],
  treeshake: false
}

Actual Behaviour

Namespaced imports are exported correctly.

It seems to work in the REPL (but cant control preserve modules), so not sure what's happening here: https://rollupjs.org/repl/?version=2.53.0&shareable=JTdCJTIybW9kdWxlcyUyMiUzQSU1QiU3QiUyMm5hbWUlMjIlM0ElMjJtYWluLmpzJTIyJTJDJTIyY29kZSUyMiUzQSUyMmV4cG9ydCUyMColMjBhcyUyMG90aGVyJTIwZnJvbSUyMCcuJTJGb3RoZXIuanMnJTNCJTIyJTJDJTIyaXNFbnRyeSUyMiUzQXRydWUlN0QlMkMlN0IlMjJuYW1lJTIyJTNBJTIyb3RoZXIuanMlMjIlMkMlMjJjb2RlJTIyJTNBJTIyZXhwb3J0JTIwZnVuY3Rpb24lMjBmb28oKSUyMCU3QiU3RCU1Q25leHBvcnQlMjBmdW5jdGlvbiUyMGJhcigpJTIwJTdCJTdEJTIyJTdEJTVEJTJDJTIyb3B0aW9ucyUyMiUzQSU3QiUyMmZvcm1hdCUyMiUzQSUyMmNqcyUyMiUyQyUyMm5hbWUlMjIlM0ElMjJteUJ1bmRsZSUyMiUyQyUyMmFtZCUyMiUzQSU3QiUyMmlkJTIyJTNBJTIyJTIyJTdEJTJDJTIyZ2xvYmFscyUyMiUzQSU3QiU3RCU3RCUyQyUyMmV4YW1wbGUlMjIlM0FudWxsJTdE

@milesj milesj changed the title Exported "namespaced imports" are lost Exported "namespaced imports" are lost when preserveModules is true? Jul 9, 2021
@milesj
Copy link
Author

milesj commented Jul 9, 2021

I created a test repo and this seems to happen regardless of preserveModules setting: https://github.com/milesj/rollup-preserve-namespaces

@milesj milesj changed the title Exported "namespaced imports" are lost when preserveModules is true? Exported "namespaced imports" are lost Jul 9, 2021
@lukastaegert
Copy link
Member

I can confirm there is a bug and it is triggered by treeshake: false. If you set this to true, everything should work. I will see if I can figure out what the issue is.

@lukastaegert
Copy link
Member

Fix at #4175

@milesj milesj changed the title Exported "namespaced imports" are lost Exported "namespaced imports" are lost when treeshake is false Jul 11, 2021
@milesj
Copy link
Author

milesj commented Jul 11, 2021

@lukastaegert Awesome, thanks for the quick fix!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants