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

Extends in tsconfig.json not working #913

Closed
mjbeswick opened this issue Mar 4, 2021 · 4 comments
Closed

Extends in tsconfig.json not working #913

mjbeswick opened this issue Mar 4, 2021 · 4 comments

Comments

@mjbeswick
Copy link

There seems to be an issue with esbuild not importing the content of the file "extends".

Example:

tsconfig.json

{
  "extends": "./tsconfig.paths.json",
}

tsconfig.paths.json

{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "paths": {
      "#/*": ["./*"]
    }
  }
}

Paths in "tsconfig.paths.json" are not resolved when building with "tsconfig.json".

@evanw
Copy link
Owner

evanw commented Mar 4, 2021

I just tried this and it appears to work fine for me. Here's what I tried:

const esbuild = require('esbuild')
const fs = require('fs')
fs.mkdirSync('src')
fs.writeFileSync('src/entry.ts', `
  import {x} from '#/import'
  console.log(x)
`)
fs.writeFileSync('src/import.ts', `
  export let x = 'works'
`)
fs.writeFileSync('src/tsconfig.json', `{
  "extends": "./tsconfig.paths.json"
}`)
fs.writeFileSync('src/tsconfig.paths.json', `{
  "extends": "../tsconfig.base.json",
  "compilerOptions": {
    "paths": {
      "#/*": ["./*"]
    }
  }
}`)
fs.writeFileSync('tsconfig.base.json', `{
}`)
esbuild.buildSync({
  entryPoints: ['src/entry.ts'],
  outfile: 'out.js',
  bundle: true,
})
require('./out')

That prints works. Is there anything else about your setup that is required to reproduce this issue?

@mjbeswick
Copy link
Author

@evanw here is a modified version of which replicates the issue I was having:

const esbuild = require('esbuild');
const fs = require('fs');

if (!fs.existsSync('src')) fs.mkdirSync('src');

fs.writeFileSync('src/entry.ts', `
  import {x} from '#/import'
  console.log(x)
`);

fs.writeFileSync('src/import.ts', `
  export let x = 'works'
`);

fs.writeFileSync('tsconfig.json', `{
  "extends": "./tsconfig.paths.json",
  "compilerOptions": {
    "baseUrl": "./src"
  }
}`);

fs.writeFileSync('tsconfig.paths.json', `{
  "extends": "./tsconfig.base.json",
  "compilerOptions": {
    "paths": {
      "#/*": ["./*"]
    }
  }
}`);

fs.writeFileSync('tsconfig.base.json', `{}`);

esbuild.buildSync({
    tsconfig: './tsconfig.json',
    entryPoints: ['src/entry.ts'],
    outfile: 'out.js',
    bundle: true
});

require('./out');

The tsconfig in the project I'm working on had compilerOptions.baseUrl = "./src" which cases the problem, even though "tsconfig.paths.json" imported using extends is in the the same directory. If I add the same baseUrl to "tsconfig.paths.json", the issue goes away.

Another issue I notice is that esbuild requires the extends path to be prefixed with "./", else the files isn't found. I'm assuming it's an issue, as tsc works without? I checked the documentation [https://www.typescriptlang.org/tsconfig#extends] and it doesn't say that if current directory prefix is required, although the examples there include it.

@evanw
Copy link
Owner

evanw commented Mar 5, 2021

Thanks for the additional information. It looks like the explicit base URL should take precedence over the implicit base URL that is generated when using the new "paths without baseUrl" feature added in TypeScript 4.1. I'll fix this in the next release.

@evanw
Copy link
Owner

evanw commented Mar 5, 2021

Another issue I notice is that esbuild requires the extends path to be prefixed with "./", else the files isn't found. I'm assuming it's an issue, as tsc works without? I checked the documentation [https://www.typescriptlang.org/tsconfig#extends] and it doesn't say that if current directory prefix is required, although the examples there include it.

Omitting the leading ./ with the TypeScript compiler doesn't work for me:

$ echo '{}' > tsconfig.base.json
$ echo 'console.log()' > example.ts

$ echo '{"extends": "./tsconfig.base.json"}' > tsconfig.json
$ tsc

$ echo '{"extends": "tsconfig.base.json"}' > tsconfig.json
$ tsc
tsconfig.json:1:13 - error TS6053: File 'tsconfig.base.json' not found.

1 {"extends": "tsconfig.base.json"}
              ~~~~~~~~~~~~~~~~~~~~


Found 1 error.

This is the actual TypeScript compiler code that I'm referencing:

    function getExtendsConfigPath(
        extendedConfig: string,
        host: ParseConfigHost,
        basePath: string,
        errors: Push<Diagnostic>,
        createDiagnostic: (message: DiagnosticMessage, arg1?: string) => Diagnostic) {
        extendedConfig = normalizeSlashes(extendedConfig);
        if (isRootedDiskPath(extendedConfig) || startsWith(extendedConfig, "./") || startsWith(extendedConfig, "../")) {
            let extendedConfigPath = getNormalizedAbsolutePath(extendedConfig, basePath);
            if (!host.fileExists(extendedConfigPath) && !endsWith(extendedConfigPath, Extension.Json)) {
                extendedConfigPath = `${extendedConfigPath}.json`;
                if (!host.fileExists(extendedConfigPath)) {
                    errors.push(createDiagnostic(Diagnostics.File_0_not_found, extendedConfig));
                    return undefined;
                }
            }
            return extendedConfigPath;
        }
        // If the path isn't a rooted or relative path, resolve like a module
        const resolved = nodeModuleNameResolver(extendedConfig, combinePaths(basePath, "tsconfig.json"), { moduleResolution: ModuleResolutionKind.NodeJs }, host, /*cache*/ undefined, /*projectRefs*/ undefined, /*lookupConfig*/ true);
        if (resolved.resolvedModule) {
            return resolved.resolvedModule.resolvedFileName;
        }
        errors.push(createDiagnostic(Diagnostics.File_0_not_found, extendedConfig));
        return undefined;
    }

Basically it says "if path starts with / or ./ or ../ then resolve as a relative file, otherwise resolve as a package file". So omitting the leading ./ causes it to be interpreted as a package file and node_modules directories are searched instead.

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

Successfully merging a pull request may close this issue.

2 participants