Aidin
Aidin

Reputation: 29927

Sharing file between commonjs and ejs, Typescript is not a module.ts(2306) on commonjs file

Background

I have a file that I need to share between the two repositories. The file contains one single object.

I know that if I use modules.export = myObject it only works for the commonjs repo (repo A), and if I do export = myObject or export default myObject it only works for the ES6 repo (repo B).

Questions:

  1. Is this possible to make the same file work for both systems? I tried export = modules.export = myObject or any other combination but none works.

  2. How can I import the commonjs version in ES6 repo? I tried leaving it a commonjs modules.export = myObject file, but in Repo B when I do import foo from "path/to/file" it keeps crying the following message. it goes away the moment I change it to export = myObject, but then it wouldn't work for Repo A.

File '/path/to/file.ts' is not a module.ts(2306)

Notes

  1. I found that setting "esModuleInterop": true in my tsconfig.json should make it work, but no matter what I do, (any "module" value in tsconfig.json, turning on allowSyntheticDefaultImports) it doesn't work. I keep getting the same 2306 error.
  2. As I mentioned in the comment, it can be considered that the file contains a static object. (e.g. {a: 5, b: 8};)

tsconfig.json

The part in repo A that I am touching is the config file for Quasar and it's in vanilla JS.

Repo B is Typescript/Node project, and the relevant part of the tsconfig.json in there is:

"compilerOptions": {
        "baseUrl": ".",
        "esModuleInterop": true,
        "experimentalDecorators": true,
        "module": "commonjs",
        "moduleResolution": "node",
        "outDir": "dist",
        "strict": true,
        "sourceMap": true,
        "target": "es6",
        ...

Upvotes: 2

Views: 1312

Answers (3)

Aidin
Aidin

Reputation: 29927

I just found that as long as the file is a .json file with proper JSON format, one can use it both in ES6 (import) and CommonJS (require).

Per this answer (and this article), for ES6 you need to enable "resolveJsonModule": true, in tsconfig.json and then you can have the content in both formats.

So if the file content.json has the following content (with no comment or anything else):

{
   "age": 34
}

then we will have ...

import * as jsonContent from "../../content.json"

console.log(jsonContent.age); // prints out 34

as well as

const jsonContent = require("../../content.json");

console.log(jsonContent.age); // prints out 34

They both print out the content successfully!

Upvotes: 2

Griffork
Griffork

Reputation: 682

I exprimented a bit more and made another solution that you might prefer.

I still haven't figured out how to get typing on common-js style imports, but the import does work if run and doesn't cause compiler errors.


I moved the shared code into it's own project and made it compile to a different directory. This is because the "esModuleInterop" flag does not work if importing a typescript file, so instead I compiled the javascript file with a supporting .d.ts file to another directory so that the source .ts file doesn't cause a conflict.

Shared tsconfig.json:

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "ES5",
        "lib": [ "ES5" ],
        "declaration": true,
        "outDir": "../sharedOut"
    }
}

Shared.ts:

export = {'a': 1, 'b': 2};

And in the ouput folder you should get:

Shared.js:

"use strict";
module.exports = { 'a': 1, 'b': 2 };

Shared.d.ts

declare const _default: {
    a: number;
    b: number;
};
export = _default;

This causes you commonjs import to look like this:

commonjs.ts

var val = require("../sharedOut/shared");

console.log(val);

or (if you can use ES6-style imports but need the output to be commonjs):

import val from "../sharedOut/shared";

console.log(val);

Both of the above files were compiled with the following tsconfig:

tsconfig.json

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es5",
        "lib": [ "es5" ],
        "types": ["node"],
        "esModuleInterop": true
    }
}


The ES6 import is (again) pretty straight-forward:

es6.ts

import shared from "../shared/shared";

console.log(shared);

tsconfig.json:

{
    "compilerOptions": {
        "module": "ES6",
        "target": "es6",
        "lib": [ "es6" ],
        "esModuleInterop": true
    }
}


Again if this doesn't address your issue I'll need more information to progress. I'd need your tsconfig settings for each project as the version of the typescript compiler you're using for each project to make a more accurate answer.

Upvotes: 0

Griffork
Griffork

Reputation: 682

I have found a solution, however the import syntax in commonjs land is not the nicest, but I hope it's usable for you.

You've provided no information to how your projects are setup other than specifying commonjs and es6 style imports so I'm going out on a limb here. Also worth noting that while including the files worked, when using commonjs-style imports they were typed as rather than retaining their typescript typings (I was not able to get typing using commonjs-style imports (require) - I haven't used this import syntax for a while so the correct tsconfig.json options to make it work escape me).


I moved the shared code into it's own project and compiled it with the following tsconfig settings:

Shared tsconfig.json:

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "ES5",
        "lib": [ "ES5" ],
    }
}

Shared.ts:

export default {'a': 1, 'b': 2};

Shared.js (output):

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = { 'a': 1, 'b': 2 };

(Note that this output is commonjs style, because ES6 is compatible with commonjs files).


When using require directly you need to reference the .default variable on the imported file:

commonjs.ts

var val = require("../shared/shared").default;

console.log(val);

However if you are configured so that you can write ES6-style imports and output to commonjs you can use the following syntax instead (this will provide you with typing information):

commonjs.ts

import val from "../shared/shared";

console.log(val);

Both of the above files were compiled with the following tsconfig:

tsconfig.json

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es5",
        "lib": [ "es5" ],
        "types": ["node"]
    }
}

The ES6 import is pretty straight-forward:

es6.ts

import shared from "../shared/shared";

console.log(shared);

tsconfig.json:

{
    "compilerOptions": {
        "module": "ES6",
        "target": "es6",
        "lib": [ "es6" ]
    }
}

I have a sneaking suspicion this doesn't 100% address your issue, but I'd need your tsconfig settings for each project as the version of the typescript compiler you're using for each project to make a more accurate answer.

Upvotes: 0

Related Questions