Skip to content

Import removed when required by JSX "preserve" #24731

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

Closed
marcusball opened this issue Jun 6, 2018 · 7 comments
Closed

Import removed when required by JSX "preserve" #24731

marcusball opened this issue Jun 6, 2018 · 7 comments
Labels
Needs More Info The issue still hasn't been fully clarified

Comments

@marcusball
Copy link

I really, really hope I verified this correctly. I found #24551, #24729, #15472, #12226, etc, but I'm still getting this issue on the nightly version. Sorry in advance if I just misconfigured this or did something incredibly dumb.

TypeScript Version: 3.0.0-dev.20180606

Search Terms: import removed jsx

Code

Repo here: https://github.com/marcusball/tsc-transpile-remove-jsx-import

src/index.tsx:

import { h, app } from 'hyperapp';

//const _ = h(''); // Works properly if this is uncommented 

const root = document.getElementById('root') as HTMLElement;

const view = () => (
    <h1>Hello world</h1>
);

app({}, {}, view, root);

package.json:

{
    "devDependencies": {
        "typescript": "^3.0.0-dev.20180606"
    },
    "scripts": {
        "build": "rm -rf build && tsc"
    },
    "dependencies": {
        "hyperapp": "^1.2.6",
        "tslib": "^1.9.2"
    }
}

tsconfig.json:

{
    "compilerOptions": {
        "target": "es5",
        "module": "ESNext",
        "lib": [
            "dom",
            "es2015",
            "es2015.collection"
        ],
        "jsx": "preserve",
        "sourceMap": false,
        "outDir": "build",
        "importHelpers": true,
        "strict": true,
        "moduleResolution": "node",
        "esModuleInterop": true
    },
    "include": [
        "src"
    ]
}

Expected behavior:

The h import should not be removed. Expected output would be:

import { h, app } from 'hyperapp';
// var _ = h(''); // Works properly if this is uncommented 
var root = document.getElementById('root');
var view = function () { return (<h1>Hello world</h1>); };
app({}, {}, view, root);

Actual behavior:

The h import is removed.

import { app } from 'hyperapp';
// const _ = h(''); // Works properly if this is uncommented 
var root = document.getElementById('root');
var view = function () { return (<h1>Hello world</h1>); };
app({}, {}, view, root);

Playground Link:

Related Issues:
#24551 maybe? It seems very similar but this issue is still appearing on @next (3.0.0-dev.20180606).

Note:
I also tried adding "jsxFactory": "h" to my tsconfig file, but the issue still occurred (even with all three values of jsx).

@marcusball
Copy link
Author

The more I think about it, the more it seems that this might not be a fixable issue. Since TypeScript doesn't know what function I intend to use as my JSX factory (unless it was to parse my .babelrc) would that mean that the only fix would be if TS did not perform any unused imports checks? Though, if that's the case, I'm not sure how "jsx": "preserve" would actually usable.

@mhegazy
Copy link
Contributor

mhegazy commented Jun 6, 2018

I also tried adding "jsxFactory": "h" to my tsconfig file, but the issue still occurred (even with all three values of jsx).

I have tried this locally and i do not see any issues...

here is what i see:

c:\test\24731>tree /F .
│   package.json
│   tsconfig.json
│
├───node_modules
│   └───hyperapp
│       │   hyperapp.d.ts
│       │   LICENSE.md
│       │   package.json
│       │   README.md
│       │
│       ├───dist
│       │       hyperapp.js
│       │       hyperapp.js.gz
│       │       hyperapp.js.map
│       │
│       └───src
│               .DS_Store
│               index.js
│
└───src
        index.tsx

c:\test\24731>type tsconfig.json
{
    "compilerOptions": {
        "target": "es5",
        "module": "ESNext",
        "lib": [
            "dom",
            "es2015",
            "es2015.collection"
        ],
        "jsx": "preserve",
        "jsxFactory": "h",
        "sourceMap": false,
        "outDir": "build",
        "importHelpers": true,
        "strict": true,
        "moduleResolution": "node",
        "esModuleInterop": true
    },
    "include": [
        "src"
    ]
}

c:\test\24731>type src\index.tsx
import { h, app } from 'hyperapp';

//const _ = h(''); // Works properly if this is uncommented

const root = document.getElementById('root') as HTMLElement;

const view = () => (
    <h1>Hello world</h1>
);

app({}, {}, view, root);

c:\test\24731>tsc -v
Version 2.9.1

c:\test\24731>tsc

c:\test\24731>type build\index.jsx
import { h, app } from 'hyperapp';
//const _ = h(''); // Works properly if this is uncommented
var root = document.getElementById('root');
var view = function () { return (<h1>Hello world</h1>); };
app({}, {}, view, root);

@mhegazy mhegazy added the Needs More Info The issue still hasn't been fully clarified label Jun 6, 2018
@marcusball
Copy link
Author

I am incredibly confused. I spent hours trying and retrying these options, but after you said that I went back and retried it several more times, and then it finally worked. Sorry for the trouble, thank you taking the time to took at this.

@mrnagydavid
Copy link

Sorry to be a nuisance, but I'm afraid this problem still exists. Maybe there is a setting for this?

Here's a repo: https://gitlab.com/mrnagydavid/hyperapp-typescript-test

First the "normal" approach that results in an error. Then I show how it can be solved by explicitly using the otherwise deleted import.

Problem

view.tsx:

import { h, View } from "hyperapp"; // <- h is an unused import so it gets stripped :(
import { State } from "./state";
import { Actions } from "./actions";

const view: View<State, Actions> = (state, actions) => (
  <div oncreate={() => console.log("Hurrá!")}>
    <h1>{state.counter.count}</h1>
    <button onclick={() => actions.counter.down(1)}>-</button>
    <button onclick={() => actions.counter.up(1)}>+</button>
  </div>
);

export default view;

Results in:

ReferenceError: h is not defined

"counter/view.tsx":[function(require,module,exports) {
"use strict";
exports.__esModule = true;
var hyperapp_1 = require("hyperapp"); 
var view = function view(state, actions) {
  return h("div", {
    oncreate: function oncreate() {
      return console.log("Hurrá!");
    }
  }, h("h1", null, state.counter.count), h("button", {
    onclick: function onclick() {
      return actions.counter.down(1);
    }
  }, "-"), h("button", {
    onclick: function onclick() {
      return actions.counter.up(1);
    }
  }, "+"));
};

Workaround

view.tsx:

import { h as _h, View } from "hyperapp";
import { State } from "./state";
import { Actions } from "./actions";

const h = _h;  // <- we make h to be available for after JSX is transpiled

const view: View<State, Actions> = (state, actions) => (
  <div oncreate={() => console.log("Hurrá!")}>
    <h1>{state.counter.count}</h1>
    <button onclick={() => actions.counter.down(1)}>-</button>
    <button onclick={() => actions.counter.up(1)}>+</button>
  </div>
);

export default view;

@marcusball
Copy link
Author

So, this is pretty much the same mystery from when I opened the issue. I just tried cloning your repo and yarn build resulted in no errors, so it seems to be working fine for me.

Maybe try running npm rebuild and mess around with caching options when building. It seems like it will start working once you start pulling your hair out trying to figure out what the problem is.

@mrnagydavid
Copy link

mrnagydavid commented Nov 10, 2018

tl;dr: This line is essential in tsconfig.json:
"module": "ESNext",

I tried the tsconfig.json above in your post. It worked. Then I started to comment-out the lines. It stopped working after removing the modules property. I put this one single line into my original tsconfig.json and lo, and behold! it's working.

@elbow-jason
Copy link

It occurred to me that the React happy path is being well-respected or else there would be more people with this problem. Hilariously, this is my solution...

./tsconfig.json

{
    "compilerOptions": {
        "module": "commonjs",
        "strict": true,
    },
    "exclude": [
        "node_modules"
    ]
}

./src/engine/react.ts

import { html } from 'snabbdom-jsx'
export const createElement = html

first line of ./src/some_component.tsx

import * as React from "./engine/react"

Zantier added a commit to Zantier/webtest that referenced this issue Jun 26, 2021
Note that I have typescript preserving the jsx, leaving the
transpilation to babel, as suggested here:
https://preactjs.com/guide/v10/typescript/
But it turns out that typescript has a bug:
microsoft/TypeScript#24731
that means it wasn't preserving the "h" import from preact, which is
needed for the jsx elements. But there seems to be a workaround of
setting "module": "ESNext" in tsconfig, and this seems to also require
setting "moduleResolution". Also, typescript requires that files with jsx
in be named .tsx.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs More Info The issue still hasn't been fully clarified
Projects
None yet
Development

No branches or pull requests

4 participants