2

I am working on a project consisting of three parts: a Client, a Server and a Common directory which contains things I want to import from both the Client and the Server. Everything can use both JS and TS. (Thanks to the babel-typescript preset)

Directory structure

Here is how it looks like:

root/
├── babel.config.js
├── Common/
│   ├── helper1.ts
│   ├── helper2.ts
│   ├── helper3.js
├── Client/
│   ├── src/
│   │   └── file1.js
│   └── .babelrc.js
└── Server/
    ├── src/
    │   └── file1.js
    └── .babelrc.js

Babel config files

Here is what my root/babel.config.js looks like:

module.exports = {
    presets: ["@babel/preset-typescript"],
    plugins: [
        ["@babel/plugin-transform-for-of", { assumeArray: true }],
        "@babel/plugin-syntax-dynamic-import",
        "@babel/plugin-syntax-import-meta",
        "@babel/plugin-proposal-class-properties",
        "@babel/plugin-proposal-json-strings",
        ["@babel/plugin-proposal-decorators", { legacy: true }],
        "@babel/plugin-proposal-function-sent",
        "@babel/plugin-proposal-export-namespace-from",
        "@babel/plugin-proposal-numeric-separator",
        "@babel/plugin-proposal-throw-expressions",
        "@babel/plugin-proposal-export-default-from",
        "@babel/plugin-proposal-logical-assignment-operators",
        "@babel/plugin-proposal-optional-chaining",
        ["@babel/plugin-proposal-pipeline-operator", { proposal: "minimal" }],
        "@babel/plugin-proposal-nullish-coalescing-operator",
        "@babel/plugin-proposal-do-expressions",
        "@babel/plugin-proposal-function-bind",
    ],
};

And here is what my Server/.babelrc.js looks like:

const moduleAlias = require("./tools/module-alias");
const rootConfig = require("../babel.config");

module.exports = {
    presets: [
        ...rootConfig.presets,
        [
            "@babel/preset-env",
            {
                targets: {
                    node: "current",
                },
                exclude: ["transform-for-of"],
            },
        ],
    ],
    plugins: [
        ...rootConfig.plugins,
        [
            "babel-plugin-module-resolver",
            {
                root: ["."],
                alias: moduleAlias.relativeAliases,
                extensions: [".js", ".ts"],
            },
        ],
    ],
};

I will omit the Client/.babelrc.js since it's very similar to the Server one.

Basic test files

Here is an example Common/helper3.js file:

function doubleSay(str) {
    return `${str}, ${str}`;
}

function capitalize(str) {
    return str[0].toUpperCase() + str.substring(1);
}

function exclaim(str) {
    return `${str}!`;
}

const result = "hello" |> doubleSay |> capitalize |> exclaim;

console.log(result);

And inside Server/src/index.js I just import the file Common/helper3.js.

The error

Then, inside the Server directory, I do this:

npx babel-node src/index.js  -x .ts,.js

Which prints the following error:

const result = "hello" |> doubleSay |> capitalize |> exclaim;
                        ^

SyntaxError: Unexpected token >

I am definitely sure this error is related to my "strange" directory structure since it's fine when I put this exact file under Server/src.

The question

How can I keep this directory structure and tell Babel to use a config when it processes files within the Common directory?

I don't use Lerna or anything. I have setup special aliases that resolve $common to ../Common where needed. I know there is no issue with this since the file is properly found by Babel (otherwise I would get a "File not found" error)

Note

This babel structure is one of my attempt to fix the issue above. Originally I only had one babel.config.js inside Server and another inside Client. I thought having one at the root would solve this problem but it didn't change anything.

Edit after searching a lot more:

After taking a look at the babel code to find the config parsing, I noticed that this line : https://github.com/babel/babel/blob/8ca99b9f0938daa6a7d91df81e612a1a24b09d98/packages/babel-core/src/config/config-chain.js#L456 is called (null is returned).
I printed everything in this scope and noticed that babel automatically generates an only parameter containing the cwd. (Effectively saying that my babel.config.js doesn't affect my common directory despite being "above" is in the directory hierarchy).
I decided to try overloading it in the command line and arrived at this command:

npx babel-node src/index.js --root-mode upward -x .ts,.js --only .,../Common/ --ignore node_modules

(Added --only and --ignore)
This made me progress a bit: instead of failing to parse advanced syntax (pipeline operator) in js files, it failed on a ts failing, saying

export const accountStatus = Object.freeze({
^^^^^^

SyntaxError: Unexpected token export

What I don't understand is how it can parse the pipeline operator but not the typescript file even though both the pipeline plugin and the typescript are inside the same babel.config.js

Edit after solving this last issue:

Adding --only and --ignore made it work. The other issue was because I forgot to add the @babel/plugin-transform-modules-commonjs plugin and it was not able to resolve the import.

The slight change I did was adding ignore: ["**/node_modules"], to my root babel.config.js file and change my command to use those arguments: --root-mode upward -x .ts,.js --ignore __fake__.

Adding a random --ignore is enough to prevent babel from guessing by itself.

This is the solution I use and it works fine even though it's not very elegant.

Telokis
  • 3,399
  • 14
  • 36
  • If not mistaken you can specify an `enviroment` for either develop and deploy. For each environment you can specify specific plugins etc. so you don't need to keep 2 seperate config files – SuperDJ Apr 20 '19 at 10:46
  • Possible duplicate: https://stackoverflow.com/questions/34924581/how-do-you-configure-babel-to-run-with-different-configurations-in-different-env – SuperDJ Apr 20 '19 at 10:49
  • Yes, I know about that but how would it solve my issue? The issue is that `babel-node` doesn't transpile files inside my `Common` directory, even when imported from within `Server`. It acts as if there were no config specified at all. – Telokis Apr 20 '19 at 10:59
  • The babelrc.js file should be on the root of the project. Also bablerc.js and babel.config are the same file/ do the same. So I guess it is better to have all of them combined into one config or babelrc file at the root – SuperDJ Apr 20 '19 at 11:02
  • Yes but the reason I have multiple ones is because the Client and Server don't use the same plugins/presets. Using `environment` for this would feel more like a hack, I think. – Telokis Apr 20 '19 at 11:04
  • This babel structure is one of my attempt to fix the issue above. Originally I only had one `babel.config.js` inside `Server` and another inside `Client`. I thought having one at the root would solve this problem but it didn't change anything. – Telokis Apr 20 '19 at 11:19

0 Answers0