TypeStrong / ts-loader

TypeScript loader for webpack
https://johnnyreilly.com/ts-loader-goes-webpack-5
MIT License
3.45k stars 428 forks source link

An example of this working with Babel/ES6, Webpack, HMR, Typescript would be super helpful:) #101

Closed hydrotik closed 8 years ago

hydrotik commented 8 years ago

I've been combing through the repo, the issues, and your blog posts and I seem to be missing something here so forgive the rudimentary "issue" == support. Because there are so many moving parts to this tooling setup, it's harder for people to get up to speed with this and I could use a bit of help which hopefully might help the next person:) Currently in my setup I have the following:

Webpack config taking out react-hot doesn't seem to help.

{
            test: /\.ts(x?)$/,
            loader: 'react-hot!babel-loader!ts-loader',
            exclude: /node_modules/
}

tsconfig.json

{
    "compilerOptions": {
        "target" : "es6",
        "jsx" : "react"
    },
    "files": [
        "typings/react/react.d.ts"
    ]
}

app.jsx (my facade - Note it is in regular es6 jsx)

import 'babel-polyfill';
import 'normalize.css/normalize.css';

//import '../node_modules/slick-carousel/slick/slick.css';
//import '../node_modules/slick-carousel/slick/slick-theme.css';

import './scss/app.scss';

import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App/App';

ReactDOM.render(
  <App />,
  document.getElementById('app')
);

My App component in .tsx in ./components/App/App

import * as React from 'react';

const Component = React.Component;

const displayName = 'App';

interface Props {
    foo: string;
}

class App extends Component<Props, {}> {

    componentDidMount() {
    }

    componentWillUnmount() {
    }

    render() {

        return (
            <div className = "app">
                HELLO WORLD
            </div>
        );
    }
}

And I am still getting the error:

[1] ERROR in ./src/global/client/components/App/App.tsx
[1] Module parse failed: /myprojectname/node_modules/react-hot-loader/index.js!/myprojectname/node_modules/babel-loader/index.js!/myprojectname/node_modules/ts-loader/index.js!/myprojectname/src/global/client/components/App/App.tsx Line 3: Unexpected token
[1] You may need an appropriate loader to handle this file type.
[1] | /* REACT HOT LOADER */ if (module.hot) { (function () { var ReactHotAPI = require("/myprojectname/node_modules/react-hot-loader/node_modules/react-hot-api/modules/index.js"), RootInstanceProvider = require("/myprojectname/node_modules/react-hot-loader/RootInstanceProvider.js"), ReactMount = require("react/lib/ReactMount"), React = require("react"); module.makeHot = module.hot.data ? module.hot.data.makeHot : ReactHotAPI(function () { return RootInstanceProvider.getRootInstances(ReactMount); }, React); })(); } try { (function () {
[1] | 
[1] | import * as React from 'react';
[1] | const Component = React.Component;
[1] | const displayName = 'App';
[1]  @ ./src/global/client/app.jsx 19:11-42
[1] webpack: bundle is now VALID.
hydrotik commented 8 years ago

And SASS too would be great;)

johnnyreilly commented 8 years ago

Hi, you might find this a useful reference: https://github.com/Microsoft/TypeScriptSamples/tree/master/es6-babel-react-flux-karma

jbrantly commented 8 years ago

so forgive the rudimentary "issue" == support

Not a problem!

I would love to get an example going like you mentioned (it's the long term goal of my blog post series). However, I honestly don't see having the time to get all of that done anytime soon. I can give you a few tips though.

Regarding the error you're seeing, it looks like Babel isn't transforming your source for some reason. I honestly don't know why that would be. Is it possible Babel is configured elsewhere to not do certain transformations?

Regarding an example with hot reloading, https://github.com/keokilee/react-typescript-boilerplate recently made some rounds on twitter. It's using the new pattern for hot reloading (using a Babel transform instead of react-hot-loader).

hydrotik commented 8 years ago

Thank you both! I will take a look at these and report my findings :) :+1:

hydrotik commented 8 years ago

There is a lot of great information in both of the examples you shared. I made some progress incorporating a couple practices such as referencing the tsd and modifications to my tsconfig. At this point I am getting the dreaded invalid token error using this code, although I seem to be getting a new error that I am unsure of.

./app.tsx

/// <reference path="../../../typings/tsd.d.ts" />

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import * as App from './components/app/App';

ReactDOM.render(<App />, document.getElementById('app'));

./components/App/App.tsx

/// <reference path="../../../../../typings/tsd.d.ts" />

import * as React from 'react';

export default class App extends React.Component<{}, {}> {
    public render(): React.ReactElement<{}> {
        return (<div>
            HELLO WORLD
        </div>
        );
    }
}

Which is throwing:

[1] ERROR in ./src/global/client/app.tsx
[1] (7,18): error TS2604: JSX element type 'App' does not have any construct or call signatures.
[1] 
[1] ERROR in ./src/global/client/app.tsx
[1] Module build failed: SyntaxError: /myprojectname/node_modules/ts-loader/index.js!/myprojectname/src/global/client/app.tsx: Unexpected token (4:16)
[1]   2 | import * as ReactDOM from 'react-dom';
[1]   3 | import * as App from './components/app/App';
[1] > 4 | ReactDOM.render(<App />, document.getElementById('app'));

Also something to note is that when I change my tsconfig.json from "jsx" : "preserve" to "jsx" : "react" I get the appropriate loader error. Seems preserve is the right way as I want Babel to handle the jsx transform?

johnnyreilly commented 8 years ago

I notice you're using React-DOM which implies that you're using React 0.14.x.

To my knowledge the latest typings for React on Definitely Typed are 13.3. I think @jbrantly is working on the 0.14.x Typings at present. This might be throwing you for a loop in the meantime..

hydrotik commented 8 years ago

Thanks for that catch! I will keep my eye out for https://github.com/DefinitelyTyped/DefinitelyTyped/pull/6205

basarat commented 8 years ago

You have

export default class App 

And you import it as (which is wrong):

import * as App from './components/app/App';

default imports should be done as import App from './components/app/App'. Hence the error JSX element type 'App' does not have any construct or call signatures .

Note : your IDE should already be highlighting this as an error. If not give atom-typescript a go :P (shameless plug :rose:)

PS: Personally I highly dislike default as I've seen it break too many times in refactoring. I'd rather export var foo / import {foo} from ...

hydrotik commented 8 years ago

Thank you @basarat, yes this solved my problem. However if I try to do:

export class App extends React.Component<{}, {}> {
    public render(): React.ReactElement<{}> {
        return (<div>
            HELLO WORLD
        </div>
        );
    }
}

I still get the error with import * as App from './components/app/App';, which should be valid as far as I know. I also tried without the export declaration and no change. Might have something to do with my current type definition pointing to .13 that @johnnyreilly mentioned above. I have Atom downloaded and I will give it a try. Still have a lot of my stuff setup in Sublime which works here and there which is most likely a fault of my own. If you recommend any packages for Sublime, feel free to share:) In the meantime I will continue with default with caution until some of the other issues get resolved with some updates and will jump back to cleaning that up.

basarat commented 8 years ago

Might have something to do with my current type definition pointing to .13 that @johnnyreilly mentioned above

Unlikely. If you have react-dom definitions (already available on DT) it would work just fine.

I still get the error with import * as App from './components/app/App'

Error message please. :rose:

hydrotik commented 8 years ago

My mistake:) It is working without default. ./app.tsx

/// <reference path="../../../typings/tsd.d.ts" />

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { App } from './components/app/App';

ReactDOM.render(<App />, document.getElementById('app'));

./components/App/App/App.tsx

/// <reference path="../../../../../typings/tsd.d.ts" />

import * as React from 'react';

export class App extends React.Component<{}, {}> {
    public render(): React.ReactElement<{}> {
        return (<div>
            HELLO WORLD
        </div>
        );
    }
}

Now all I am getting is the jsx token issue.

[1] ERROR in ./src/global/client/app.tsx
[1] Module build failed: SyntaxError: /myprojectname/node_modules/ts-loader/index.js!/myprojectname/src/global/client/app.tsx: Unexpected token (4:16)
[1]   2 | import * as ReactDOM from 'react-dom';
[1]   3 | import { App } from './components/app/App';
[1] > 4 | ReactDOM.render(<App />, document.getElementById('app'));
[1]     |                 ^
[1]   5 | 
hydrotik commented 8 years ago

Figured I would note that I do have working (with the exception of HMR), the ability to compile successfully by bypassing the babel-loader. By changing the following: tsconfig.json

{
  "compilerOptions": {
    "target": "ES5",
    "jsx": "react"
  },
  "exclude": ["node_modules", "config", "dev_server.js"]
}

webpack config

        {
            test: /\.ts(x?)$/,
            loader: 'react-hot!ts-loader',
            exclude: /node_modules/
        }

I'm now curious why the HMR isn't working as it would seem to be more efficient to bypass the babel transform step? From what I am seeing so far, HMR isn't supported yet.

https://github.com/TypeStrong/ts-loader/issues/40

Also including the babel loader and going to es5 with jsx as react works error free but again doesn't enable HMR functionality.

johnnyreilly commented 8 years ago

PS: Personally I highly dislike default as I've seen it break too many times in refactoring. I'd rather export var foo / import {foo} from ...

Destructuring is, in my opinon, the most addictive part of ES6. Looked weird when I first saw it, now I can't stop using it! :heart:

hydrotik commented 8 years ago

Forgive my deliberate repetition. I am trying to record this info so I make sure I'm not repeating my work:)

So here's where I am at currently:

  1. Using React .14, Babel 6, and Typescript 1.6 with ts-loader 0.6.0 and webpack config with react-hot!babel-loader!ts-loader and tsconfig with "target": "ES6", "jsx": "preserve" and "transform-react-jsx" in .babelrc gives me the Unexpected Token error.
  2. Using the same above with webpack react-hot!ts-loader and tsconfig with "target": "ES5", "jsx": "react" and "transform-react-jsx" in .babelrc gives me no error, but I don't have HMR
  3. Using the above with webpack config react-hot!babel-loader!ts-loader`` and tsconfig with"target": "ES5", "jsx": "react"and"transform-react-jsx"``` in .babelrc also gives me no error, but I don't have HMR

To clarify 1 and 2 above, if I edit my ./app.tsx file (which is my entry file in webpack config), I get a reload, but if I edit ./components/App/App.tsx then I don't. Also editing my sass file in import './_App.scss'; does nothing. My app.tsx file is reloading because I am using 'webpack/hot/dev-server' but when I use 'webpack/hot/only-dev-server' I get the expected:

[Warning] [HMR] The following modules couldn't be hot updated: (They would need a full reload!)
[Warning] [HMR]  - 66

Which points to:

[1]    [66] ./src/global/client/app.tsx 1.57 kB {0} [built]

So I'm left with the existing questions/tasks.

  1. Do I even pursue the need to use the Babel transform between ts-loader and what hopefully will be a working react-hot?
  2. I am going to explore this https://github.com/gaearon/react-hot-loader/issues/209 and see if this yields any results.
  3. Is it worth incorporating any of the hmr logic here http://webpack.github.io/docs/hot-module-replacement.html that is included in https://github.com/keokilee/react-typescript-boilerplate/blob/master/app/index.tsx
  4. I will continue to watch @keokilee https://github.com/keokilee/react-typescript-boilerplate project as there are issues https://github.com/keokilee/react-typescript-boilerplate/issues/23 and https://github.com/keokilee/react-typescript-boilerplate/issues/10 (which don't affect the remaining problem of HMR, but are worth tracking)
  5. In the https://github.com/keokilee/react-typescript-boilerplate project, the https://github.com/keokilee/react-typescript-boilerplate/issues/23 issue mentions https://github.com/gaearon/babel-plugin-react-transform/issues/46 I'm wondering if when issue https://github.com/gaearon/babel-plugin-react-transform/issues/46 is addressed, is it better than using "transform-react-jsx" in my .babelrc file and switch to https://github.com/gaearon/babel-plugin-react-transform. Something to note which could change with this issue is the format of https://github.com/gaearon/react-transform-boilerplate/blob/master/.babelrc
jbrantly commented 8 years ago
  1. Using React .14, Babel 6, and Typescript 1.6 with ts-loader 0.6.0 and webpack config with react-hot!babel-loader!ts-loader and tsconfig with "target": "ES6", "jsx": "preserve" and "transform-react-jsx" in .babelrc gives me the Unexpected Token error.

I have had trouble in the past with getting babel-loader to pick up on my .babelrc file. I don't remember the specifics but I did wind up just passing it to the loader manually in the webpack config by using the babel option (undocumented). Might be worth trying.

// webpack.config.js
module.exports = {
  ...
  babel: {
    ...
  }
}
  1. Using the same above with webpack react-hot!ts-loader and tsconfig with "target": "ES5", "jsx": "react" and "transform-react-jsx" in .babelrc gives me no error, but I don't have HMR
  2. Using the above with webpack config react-hot!babel-loader!ts-loader`and tsconfig with"target": "ES5", "jsx": "react"and"transform-react-jsx"`` in .babelrc also gives me no error, but I don't have HMR

Not sure, I'm not really familiar with react-hot-loader, only with the new stuff that uses Babel transformations.

  1. In the https://github.com/keokilee/react-typescript-boilerplate project, the keokilee/react-typescript-boilerplate#23 issue mentions gaearon/babel-plugin-react-transform#46 I'm wondering if when issue gaearon/babel-plugin-react-transform#46 is addressed, is it better than using "transform-react-jsx" in my .babelrc file and switch to https://github.com/gaearon/babel-plugin-react-transform. Something to note which could change with this issue is the format of https://github.com/gaearon/react-transform-boilerplate/blob/master/.babelrc

From my own experience, this would be the way to go. Just use Babel 5 for now, and once react-transform is updated then switch.

johnnyreilly commented 8 years ago

Just a minor point: there's a problem with sourcemaps in Babel 6. As @jbrantly says stick with 5 for now. It can be tracked here:

https://github.com/babel/babel/issues/2864

keokilee commented 8 years ago

Having done Node Knockout this weekend, there were more than a few instances where HMR would stop working for whatever reason. I'll have to set aside some time to investigate further.

hydrotik commented 8 years ago

So... I can't go back down to Babel 5 because I'm also using Hapi with hapi-react-views that has a version that requires Babel 6 if you are using React .14 :facepunch:

https://github.com/jedireza/hapi-react-views/issues/31

So I wait patiently as I continue to: http://i.imgur.com/o9wN0Rs.gif

jbrantly commented 8 years ago

@keokilee Definitely interested in any cases like that. I'm only aware of one issue related to hot reloading which is documented in #52. For the most part hot reloading issues have less to do with the TypeScript loader and more to do with other aspects of the process. There is a lot that has to go right to get hot reloading working, and a lot depends on what you're trying to hot reload (for instance, a React component vs a helper library) and even what you're changing within that thing (for instance, React's render method vs an event handler).

hydrotik commented 8 years ago

A little off topic, but anyone compared with awesome-typescript-loader?

Also if anyone can suggest a place to understand a good process for pulling in external libs for React (such and react-slick) into Typescript projects, i'd be eternally grateful:) @jbrantly sorry for spamming you :/

Keats commented 8 years ago

Got an example working there https://github.com/Keats/flow-typescript/tree/master/typescript if people want another example

alexgorbatchev commented 8 years ago

I have a working boilerplate that is still WIP https://github.com/alexgorbatchev/gulp-typescript-webpack-react-hotreload

johnnyreilly commented 8 years ago

My example has now been updated to Babel 6 following the fix.

jbrantly commented 8 years ago

FYI I've added the examples/boilerplates from this thread to https://github.com/TypeStrong/ts-loader/wiki/Tutorials-&-Examples and linked it from the README

I'm going to close this thread since I don't believe there are any open items in it (it kind of wondered a bit). If I'm mistaken please feel to open new issues.