microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

Home Page:https://www.typescriptlang.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

TS5055 error while import a non relative json module with resolveJsonModule option

grayflow opened this issue · comments

TypeScript Version: 3.0.0-dev.20180605

Search Terms: TS5055 resolveJsonModule

Code

tsjson> find .
.
./main.ts
./json
./json/myjson.json
./tsconfig.json
./src
./src/test.ts

tsconfig.json:

{
  "compilerOptions": {
    "target": "es5",
    "module": "es2015",
    "moduleResolution": "node",
    "baseUrl": "./",

    "resolveJsonModule": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,


    "strict": true,
    "noEmitOnError": true
  },
  "include": [
    "main.ts",
    "src",
    "json"
  ],
  "exclude": [
    "node_modules"
  ]
}

main.ts:

import "test";

src/test.ts:

import myjson from "json/myjson.json";

console.log('hello world!', myjson.message);
export {}

json/myjson.json:

{
    "message": "to be continue..."
}

Expected behavior:
compile successful

Actual behavior:
tsjson> tsc
error TS5055: Cannot write file '*************/tsjson/json/myjson.json' because it would overwrite input file.

Playground Link:

Related Issues:

the compiler when resolving a .json file will read it and write it again. so you really want to have --outDir option set to avoid the compiler overwriting you input file.

In this case the error pops out when rollup plugin calls ts.parseConfigFileTextToJson() -- not when typescript is trying to overwrite the file -- in fact typescript won't be doing any file writes at all, all transpiled code is getting fed to rollup.

I will do a workaround by providing a fake outDir value, but the check probably doesn't belong in parseConfigFileTextToJson.

@mhegazy If there is no outDir specified, why should the .json file be duplicated? It's a static resource... is there a way to do this without needing outDir?

@mhegazy, still not feel right: if import a native js module, it won't issue TS5055 error

maybe add an compile option --allowJson(and default to false) like --allowJs for js file?

still not feel right: if import a native js module, it won't issue TS5055 error

cause the compiler does not overwrite .d.ts files. so no issue here.

maybe add an compile option --allowJson(and default to false) like --allowJs for js file?

if you use --allowJs you will get the same error. once the compiler tries to write a file in the same locations a source file an error is issued. This guarantees there are no user data loss caused by the compiler overwriting an existing file.

yes , import js module and set --allowJs to true would cause TS5055 too. if we know the imported js file do not need to be transformed(compiled), we would set allowJs to false, and i think that is the point to provide allowJs option. json file/module would never need to be transformed, so why not just make them "no emit" by default (and we don't lose anything)?

if we know the imported js file do not need to be transformed(compiled)

well we do not rely know that.

And this is why ts.parseConfigFileTextToJson() might be a wrong place for the check :)

@mhegazy

the compiler when resolving a .json file will read it and write it again

If the outDir is different, that makes sense. But if not, then why try to overwrite it? .json files are closer to being treated like static assets than javascript, so there isn't a 'build artefact' equivalent here.

this issue should be solved since it makes sence to use "./" as outDir in some cases

@mhegazy why is this issue still marked 'working as intended'?

If this is intended to break all projects that don't specify outDir, then how does that make any sense?

And can you answer why the compiler is attempting to write a static file over the top of itself?

The correct solution here is to not attempt to overwrite a .json file is outDir is not specified, because the default location will be in the same directory. Seems like a pretty simple solution to me.

My actual building of the code involves a special outDir, so thankfully this isn't interfering with my deployment.

However this issue is breaking checkers which are just concerned with ensuring the code is all good and nothing more.

fuse-box/fuse-box-typechecker#53

I have proposed a PR to the a particular checker that essentially ignores all TS5055 checks as a workaround. I don't think this is a good idea, it makes sense for TS to be smarter about this.

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

Can this be re-opened and the Working as Intended label get removed?

If it is really "working as intended", I think there should be some documentation that this feature only works in conjunction with the outDir option.

It almost seems like it is not intended to be used for common use-cases like:

import { version } from './package.json'

I guess it would be fine if you could somehow ignore TS5055, but // @ts-ignore has no power here so people using bare-bones tsc are out of luck...

Another common use-case.

Let's say I have the following project structure:

package.json
src/*.ts
dist/*.js

If I specify include: src, outDir: dist and resolveJsonModule: true, and import the package.json version with import { version } from '../package.json', then suddenly my dist directory looks like this:

dist/package.json
dist/src/*.js

Even if that's "working as intended", I think that might surprise a lot of people.

Just hit this as well. The fact that the outDir option has to be set for resolveJsonModule to work properly seems like a hack. My use case is a JSON file containing test fixtures which is imported from a test suite. Now my choice is to either output test/ to something like dist-test/ (as src/ would have to be output to dist/) or simply disable well-typed JSON imports, and I'm a little bummed out about the latter being preferable.

we should be able to tell tsc to skip outputting json files when nedded with "exclude": "folderWithJson/*"

so what I did was compile to .tmp and move files to dist to achieve the desired result of excluding folders with json from typescript compilation.

tsconfig.json:

{
  "compilerOptions": {
    "baseUrl": ".",
    "outDir": ".tmp",
    "paths": {
      "@static/*": ["static/*"],
      // ...
      "@controller/*": ["src/controller/*"]
    }
  }
}

package.json:

{
  "_moduleAliases": {
    "@static": "static",
    // ...
    "@controller": "dist/controller"
  },
  // ...
  "scripts": {
    "build": "rm -rf dist/*; tsc; mv .tmp/src/* dist;"
  }
}

this way relative imports and express statics are preserved as well as all masks (from paths or _moduleAliases)

we should be able to tell tsc to skip outputting json files when nedded with "exclude": "folderWithJson/*"

I was surprised to find this doesn't work already. I've had to revert to using require instead of import to load a json due to this bug. I can't have the json copied to the outDir as it's not under my rootDir in the project, it changes the outDir structure as @MarkTiedemann shows above, and I get a TS6059 error.

When using include in tsconfig.json, if the json file is not explicitly included with include, or is explicitly listed in exclude, the json should not be copied to outDir.

Have you tried my method of outputting to a temporary folder and having npm copy contents of .tmp/src to dist?

Have you tried my method of outputting to a temporary folder and having npm copy contents of .tmp/src to dist?

I'm sure that would work for the normal build case, but I don't think it'll work when tsc is in watch mode, which I use. Ultimately, resolveJsonModule should respect the include/exclude directives.

Before I found this issue, I also left a comment on #24744 which is still open (I hadn't realized this issue is closed and marked fixed, so this discussion will likely go unnoticed).

You could use npm-watch for frontend compilation and nodemon for node apps

Having to explicitly set one option like outDir to enable other option like resolveJsonModule is not (in my opinion) something that should be marked as working as intended. There are many scenarios where, you can't have or do not want to have different outDir. For example in NativeScript the outDir is exactly the same which is resulting in that resolveJsonModule to be unusable with the latest TypeScript version.

I thing that the options should be decoupled - we shouldn't have to mandatory set one option to have another in a working state. Not to mention that you can still set the outDir` to the very same folder and cause the very same bug to reappear (which is creating a third rule - do not set outDir as the project dir...)

I'm using webpack.config.ts and I would have loved to have this feature to read the dependencies keys of my package.json. But for now, the little hack that I'm using is to read the file using Node's tools. Yeah I know not very intuitive and hacky but it is working.

'use strict';
import { readFileSync } from 'fs';
import { resolve } from 'path'; 
export default {
  // ...
  externals: Object.keys(JSON.parse(readFileSync(resolve('package.json')).toString()).dependencies),
  // ...
};

Another common use-case.

Let's say I have the following project structure:

package.json
src/*.ts
dist/*.js

If I specify include: src, outDir: dist and resolveJsonModule: true, and import the package.json version with import { version } from '../package.json', then suddenly my dist directory looks like this:

dist/package.json
dist/src/*.js

Even if that's "working as intended", I think that might surprise a lot of people.

This issue has any updates ? or workaround?

this issue should not be closed as there is still people struggling with this. Is not fixed and while working with visual studio is very annoying

If you follow this common use-case, then I have a workaround for importing package.json without mangling the output directory.

I am able to convert this problematic statement:

import pkg from '../package.json'

into

const pkg = require('../package.json')

Previously, I had used require(require.resolve('../package.json')). I have now found that extra call to be unnecessary. I don't remember why we needed require.resolve, but that at the time our program made some complaints.

One side-effect is you lose access to static type-checking as the compiler doesn't know where the dynamically imported module is from at compile-time.