1

I am trying to deprecate a module with many named exports like so:

const Foo = () => 'foo';
const Bar = () => 'bar';

export { Foo };
export { Bar };

which is fine when consuming via:

import { Foo, Bar } from './something';

My thoughts about enabling deprecation warnings was to use a default export of type object with a property getter override for each key that prints the deprecation and then returns the module.

The shape then becomes something like:

const something = {};
Object.defineProperty(something, 'Foo', {
  get(){
    console.warn('Foo is deprecated soon');
    return Foo;
  }
});
// etc
export default something;

My thinking had been that the destructuring import would figure it out so

import { Foo, Bar } from './something';

... would continue to work as before. Instead, webpack complains that something does not have a named export Foo or Bar

using this, however, works:

const { Foo, Bar } = require('./something');

I can also have import something from './something'; const { Foo, Bar } = something and that works but it defeats the purpose if I have to refactor every import that exists.

So the question really is about how import { Foo, Bar } from './something'; works compared to the require() - I'd have thought that if the default export is an object, it would figure it out and destructure, but no joy.

Is there an easy way to do this 'proxying' without changing how the exports are being consumed elsewhere?

Dimitar Christoff
  • 26,147
  • 8
  • 50
  • 69
  • Is it an option to wrap the implementation of `Foo` in a function call? – Olian04 Apr 09 '19 at 15:07
  • Unfortunately, no. The Foo is x30 old "vendor" code that's transpiled to UMD. It actually does ’export { Foo } from 'Foo';’ – Dimitar Christoff Apr 09 '19 at 15:20
  • When you say `without changing how the exports are being consumed elsewhere`, is that a requirement, or are they just being consumed in a lot of places and you don't want to go through them all? – Olian04 Apr 09 '19 at 15:37
  • correct, they are used across multiple apps - this is a son to be legacy component library that is gradually ported over 'as and when' - so looking at potentially 100+ imports. the legacy lib is now referencing a single local file that re-exports it and my desire was to decorate the components so devs get a warning and a `console.trace()` to help identify parts of the apps that will need to change. – Dimitar Christoff Apr 09 '19 at 15:49
  • I'm assuming it would not be feasible to run a regex replace on all files in these apps? (Like this one for example: https://regex101.com/r/rXa11L/2) – Olian04 Apr 09 '19 at 15:57
  • It would probably create too much noise / changes. If push comes to shove, I'd do a webpack plugin for it that checks path and logs or something, but it does not answer the core question about destructuring import from a default export that is an object. – Dimitar Christoff Apr 09 '19 at 16:04
  • I have updated the question to show the easy rewrite to `const { foo, Bar } = require('./something');` - which works. unfortunately, with eslint being as opinionated as it is, require calls need to be separate from imports and it causes a lot of issues – Dimitar Christoff Apr 09 '19 at 16:12
  • 1
    @Bergi that question is about Javascript ES6 modules. This one is about typescript modules. They are not the same thing. – Olian04 Apr 09 '19 at 16:50

1 Answers1

1

I think i made it work. Bare in mind that this is a workaround.

Given that you said that the library is being "re-exported" from a single file you could add an additional "layer" to the "re-export" where we turn the "re-exportation" file into a JS file and write our own associated typing file for it.

Working repl: https://repl.it/@Olian04/CelebratedKlutzyQuotes

Code snippets:

// index.ts
// consuming the library
import { Foo } from './demo';

console.log(Foo());
// library.ts
// the library it self
const Foo = () => 'foo';

export { Foo }
// demo.js
// the workaround implementation 
const depricatedLibrary = require('./library');

const something = new Proxy(depricatedLibrary, {
  get(obj, key) {
    if (typeof key === 'string') {
      console.warn(key + ' is deprecated soon');
    }
    return obj[key];
  }
});

module.exports = something;
// demo.d.ts
// the workaround types
export * from './library';
Olian04
  • 6,480
  • 2
  • 27
  • 54
  • yeah nice idea on the proxy - though the `export * from './library'` fails without TS given our current setup. thanks anyway, I will work it out. – Dimitar Christoff Apr 09 '19 at 16:36
  • 1
    @DimitarChristoff if you find a simple and/or satisfying solution, please ping me. I'd like to know how you solved it. Good luck! – Olian04 Apr 09 '19 at 16:43