Parcel: [TypeScript] `baseUrl` inside tsconfig.json not supporting.

Created on 11 Dec 2017  ยท  35Comments  ยท  Source: parcel-bundler/parcel

๐Ÿ› bug report

๐ŸŽ› Configuration (.babelrc, package.json, cli command)

tsconfig.json:

{
    "compilerOptions": {
        "outDir": "./dist/",
        "baseUrl": "./src",
        "sourceMap": true,
        "module": "commonjs",
        "target": "es5", 
        "removeComments": true
    }
}

๐Ÿค” Expected Behavior

structure:

- project
   - src
      - component
         Custom.ts
      Main.ts
   index.html
   tsconfig.json

component/Custom.ts:

export default function hello() {
    console.log('Hello!');
};

Main.ts:

import hello from 'component/Custom';

hello();

console should show 'Hello!'

๐Ÿ˜ฏ Current Behavior

Running from shell:

$ parcel index.html
/Users/shunia/Documents/parcel-test/src/Main.ts:3:23: Cannot resolve dependency 'components/Custom'
  1 | import hello from "components/Custom";
  2 |
> 3 | hello();

๐Ÿ’ Possible Solution

The compiler should read tsconfig.json file right, and understands that baseUrl has made the from part of import ... from ... no need to be the actual relative path from the current file.

In my case, because of baseUrl set to ./src, the original import hello from '../componet/Custom' can be replaced asimport hello from 'component/Custom'`.

This might because of the way typescript compiles the import part just as what is written in the codes:

import hello from "components/Custom";

is just compiled to:

var Custom_1 = require("components/Custom");

And then, parcel-require can not recognize the path and throws error.

๐Ÿ”ฆ Context

Our actual project are full of relative paths based on baseUrl, which we omitted the ../ parts when using relative import. With webpack and awesome-typescript-loader it works as expected. So when we are trying to get our hands on parcel, this problem really blocks us from going further.

๐ŸŒ Your Environment

| Software | Version(s)
| ---------------- | ----------
| Parcel | 1.1.0
| Node | v8.9.3
| npm/Yarn | Yarn 1.3.2
| Operating System | macOS Sierra 10.12.6

Sorry for my poor english.

Help Wanted Feature TypeScript โœจ Parcel 2 ๐Ÿ”Œ Parcel 2 Plugins

Most helpful comment

Also, related, paths in tsconfig does not seem be resolving correctly..
Did a quick test run with a current project:
Result index.tsx:15:24: Cannot resolve dependency '@client/store'
tsconfig paths example:

"paths": {
    "@store/*": [
        "src/client/app/store/*"
    ],
    "@shared/*": [
        "src/client/shared/*"
    ],
    "@client/*": [
        "src/client/*"
    ],
    "*": [
        "*"
    ]
},

All 35 comments

Also, related, paths in tsconfig does not seem be resolving correctly..
Did a quick test run with a current project:
Result index.tsx:15:24: Cannot resolve dependency '@client/store'
tsconfig paths example:

"paths": {
    "@store/*": [
        "src/client/app/store/*"
    ],
    "@shared/*": [
        "src/client/shared/*"
    ],
    "@client/*": [
        "src/client/*"
    ],
    "*": [
        "*"
    ]
},

Can someone make a simple reproduction repo triggering the error?

Yep, here we go:
Ty3uK/parcel-tsconfig-issue
Error log:

$ parcel index.html
โณ  Building...
Server running at http://localhost:1234
๐Ÿšจ  /Users/ty3uk/projects/js/parcel-test/src/Main.ts:3:23: Cannot resolve dependency '@components/Custom'
  1 | import hello from '@components/Custom';
  2 | import { bye } from 'components/NewCustom';
> 3 |
    | ^
  4 | hello();
  5 | bye();
  6 |

Note that this shouldn't be considered a bug, but a feature or enhancement, since the baseUrl and paths options in the tsconfig are only meant to affect type checking. Actual path resolution is the job of the bundler/module loader.

With that said, as a possible solution, maybe we could make use of this, by translating the options from the tsconfig into the babel plugin's config accordingly. Unless anyone else has a better idea, I could try making a solution out of that.

since the baseUrl and paths options in the tsconfig are only meant to affect type checking

Not right. If you run tsc without any files, it reads tsconfig.json and builds project, resolving paths according to baseUrl and paths sections.

That's what I'm talking about. If you run tsc or tsc -p tsconfig.json modules are resolved.

$ tsc src/Main.ts
src/Main.ts(1,19): error TS2307: Cannot find module '@components/Custom'.
src/Main.ts(2,21): error TS2307: Cannot find module 'components/NewCustom'.
$ tsc
$

Huh, I must be mistaken then, since it's never outputted the resolved module paths for me in the past unless I configured it separately outside of the tsconfig. I'll look into it some more later.

EDIT: No, I tested to make sure, and I'm not mistaken. What I meant was that typescript itself will _not_ edit the import paths in the compiled output. All baseUrl and paths do is make your code pass the typecheck, and allows TS to find the modules and give them the appropriate typings. It does nothing in compilation. Sorry if I wasn't clear.

@kingdaro Yes, the typescript compiler will not handle the path. That's why I mentioned webpack and awesome-typescript-loader in my post. So I think it is a problem that parcel could handle, to make everything right when compiling.

I tried to point this out in my post, but maybe not so clear:
parcel should make things right just as webpack+awesome-typescript-loader do, because I take parcel as a replacement of webpack+awesome-typescript-loader.

Yes, and I fully agree! Just wanted to mention that it'd be a feature instead of a bug as an aside, but I suppose that's somewhat pedantic, so ignore that part of my comment I guess ๐Ÿ˜…

@shunia I just finished implementing it on parcel-plugin-typescript#ast/path-mappings using AST transforms (so it's totally transparent to Parcel). It's highly experimental but feel free to try. When stable I will try to merge it here in the core.

@fathyb Sorry, but since I'm new to parcel, I may need a how to to get your code to test.

@shunia yarn add [email protected] or npm install [email protected] should be enough.

Just be sure you are using the latest git version of Parcel (eg. yarn add https://github.com/parcel-bundler/parcel or using yarn link).

Update: I just published it, you can just use the latest version on npm of Parcel and the plugin : yarn add parcel-bundler parcel-plugin-typescript

image
What's wrong here?

image
install without global and run by yarn run build

@shunia you are using the test version I have built for Angular AOT compilation (I'm not good with npm tags, sorry). Can you use the 0.2.0 version? ("parcel-plugin-typescript": "0.2.0", with no ^ or ~, check the version with npm ls parcel-plugin-typescript).

Also let's keep this issue about Parcel support of this feature and not my plugin bugs, please continue on the plugin issue tracker, or in Slack.

Is there a solution currently?

@atilkan it's only supported with parcel-plugin-typescript currently

@fathyb is there any example? Appritiate it.

@atilkan Just install the right version of the plugin (mostly the latest) and it's done.

Hi @fathyb, the example given by @Ty3uK still doesn't work with the latest parcel-plugin-typescript.

@dlee did you try to remove the .cache directory or launch parcel using --no-cache?

@fathyb This worked. Thank you!

So, what's the conclusion?

Any progress?
Due to baseUrl, It is not possible to use custom paths.

__Parcel:__ v1.10.3

app.tsx:3:31: Failed to install @app/store
node_modules/parcel-bundler/src/utils/installPackage.js:46:11)
    at process._tickCallback (internal/process/next_tick.js:68:7)

Reference: https://github.com/fathyb/parcel-plugin-typescript/issues/59

my team wants to use parcel, but this is preventing us from switching away from webpack.... We have a bunch of paths in our tsconfig and they can't be resolved:

error An unexpected error occurred: "https://registry.yarnpkg.com/@my-path%2factions: Not found".

I opened a separate issue #4014 but later closed it because it was basically a duplicate of this one. I thought I would mention here though that my newer issue included a link to a repro case that I created:

https://github.com/uglycoyote/parcel-import-problem

This might make it easier for Parcel developers to test that the baseUrl in tsconfig is respected once the fix is made.

We can make use of Typescript's internal module resolve, especially now that Parcel 2 supports customization of resolve.

The following code successfully resolves the target component:

const path = require("path");
const ts = require("typescript");

const root = "/test";
const config = require(path.resolve(root, "tsconfig.json"));
const compilerOptions = config.compilerOptions;

if (compilerOptions.baseUrl) {
    compilerOptions.baseUrl = path.resolve(root, compilerOptions.baseUrl);
}

const host = ts.createCompilerHost(compilerOptions);
const cache = ts.createModuleResolutionCache(root, path => path, compilerOptions);
const resolved = ts.resolveModuleName("component/Custom", "Main", compilerOptions, host, cache);
console.log(JSON.stringify(resolved));

The output is:

{"resolvedModule":{"resolvedFileName":"/test/src/component/Custom.ts","extension":".ts","isExternalLibraryImport":false},"failedLookupLocations":[]}

I think TypeScript's built-in resolver would also handle paths mapping as well.

I'll be happy to take up implementing this as a resolver for parcel 2, but will need someone to help guiding me since I'm not familiar with parcel yet. I'm looking into moving my project from webpack to parcel 2 for performance and missing paths support is a huge obstacle that I hope can help to solve.

Simple solution using babel-plugin-module-resolver:

$ yarn add --dev babel-plugin-module-resolver

Then create (or modify) a .babelrc (for total newcomers: in your root folder, where your package.json is):

{
  "plugins": [
    [
      "module-resolver",
      {
        "root": ["./src"],
        "alias": {
          "@components": "./src/components",
          "@features": "./src/features"
        }
      }
    ]
  ]
}

Note that in Parcel 2 (currently in alpha), ~ will point to the root of the project rather than wherever your index.html file is. So that means you'll have to change your imports to ~/src/.... See https://github.com/parcel-bundler/parcel/issues/2813

@mbrowne Hey, thanks for pointing that out. I'll edit my comment accordingly, but pretty much that was just a personal token choice. We can exchange ~ for something like @ with no problem.

The "simple" setup by @chiefGui worked for me:

parcel-bundler: ^1.12.4

.babelrc

{
    "plugins": [
        [
            "module-resolver",
            {
                "root": [
                    "./src"
                ],
                "alias": {
                    "~": "./src"
                }
            }
        ]
    ]
}

tsconfig.json

{
    // .......

    "compilerOptions": {
        // .......

        "baseUrl": "./",
        "paths": {
            "~/*": [
                "src/*"
            ]
        }
    }
}

A React/Typescript component example:

import * as React from "react"
import * as ReactDOM from "react-dom"

import { WorldMap } from "~/core/worldmap"
import "~/assets/main.css"

export class App extends React.Component {
    render () {
        return (
            <div>
                <WorldMap />
            </div>
        )
    }
}

ReactDOM.render(<App />, document.getElementById("root"))

@joseluisq It should work with just
"~/*", without the "~/assets/*", etc. (At least for tsconfig.json; I'm less sure about .babelrc.)

@mbrowne yeah, because I've copied and pasted. It just works for TS and Babel. So comment above updated.

@chiefGui Doesn't your solution add an additional overhead of processing ts files with Babel? I guess by default ts files are only processed with TypeScript compiler.

Was this page helpful?
0 / 5 - 0 ratings