jbrantly/ts-jsx-loader

Loading from external file

Closed this issue · 10 comments

Since this is webpack loader it could be useful to be able to read template from external file, e.g.

class MyComponent extends TypedReact.Component<{}, {}> {
    render() {
        return React.jsxFile("./templates/mycomponent.html"); 
    }
}

I'm using typed-react syntax here, but your loader could implement loading from external file like webpack/html-loader does for other resources.

I've created a pull request #8 which implements the feature, fully tested.

I'm not so sure this is the right approach. For one thing you typically need to populate the output with some data and I'm not sure how you would do that with this approach.

The other thing is that this, I believe, can already be accomplished using regular the jsx-loader and "require".

@jbrantly you are right if the jsx-loader works with require it's right way to do it. I didn't know it could could be used with TypeScript. I'm at the moment trying to use it, but getting weird errors when using it like:

module: {
    loaders: [
        {
            test: /\.ts$/,
            loader: 'typescript-loader!jsx-loader'
        }
    ]
},

It also seems to have source map support if I can figure out how to get this working.

This absolutely must work with require, but after trying a bit, it probably can't work with jsx-loader when using TypeScript.

Would you accept similar behavior as jsx-loader but with require in your loader?

P.S. I have fairly complicated application with TypeScript and browserify. All my (non-react though) templates are in external files, I can't see any problem in that. So I didn't quiet get the "populate the output with some data". The react templates are populated with data using {this.something} in the JSX syntax.

I wonder can this ever work with require on TypeScript. The webpack generates __webpack_requred__(number) function, but does not bind this nor it passes any of the variables in the function, such as crucially React or other custom components.

Using jsx-loader, how it was meant to be:

loaders: [
    {
        test: /\.ts$/,
        loader: 'typescript-loader!ts-jsx-loader'
    },
    {
        test: /\.js$/,
        loader: 'jsx-loader'
    }
]

I tried with this:

class ApplicationComponent extends TypedReact.Component<{ticks: number}, {}> {
    render() {
        return require("!!jsx!./app.html");
    }
}

Problem is the generated webpack:

var ApplicationComponent = (function (_super) {
    __extends(ApplicationComponent, _super);
    function ApplicationComponent() {
        _super.apply(this, arguments);
    }
    ApplicationComponent.prototype.render = function () {
        return __webpack_require__(5);
    };
    return ApplicationComponent;
})(TypedReact.Component);

/* 5 */
/***/ function(module, exports, __webpack_require__) {
    React.createElement("div", null, 
        React.createElement("span", {class: "msg"}, "Yello ", this.props.ticks), 
        React.createElement(Testi, null)
    )
/***/ },

One can see that React is not defined, and this is not bind. If webpack generates this kind of code when using require it can't ever work.

I misunderstood something with this originally and that's where my comment regarding populating data came from. I thought you were doing something roughly equivalent to this:

class MyComponent extends TypedReact.Component<{}, {}> {
    render() {
        return require("./templates/mycomponent.html"); 
    }
}

but in actuality you are wanting ts-jsx-loader to inline the contents of the external file which means it will have access to local variables and such.

I'm still very hesitant to include functionality like this though because it goes against React best practices. Take a look at this: https://www.youtube.com/watch?v=DgVS-zXgMTk&t=3m20s

One issue is keeping your template and your JS "in-sync". For example, if you're looking at {this.something} in your template then you need to context switch (find the corresponding JS file) to understand what's actually driving that data.

A bigger issue is something like looping. JSX has no concept of looping: instead you just use plain JavaScript.

I would recommend that you consider putting your markup directly in your components.

It's okay, I can make this as a own package, e.g. ts-external-jsx. It's only purpose is to read external JSX, it can be used in conjunction with your package, when templates are small then I see a reason to write them as a strings.

I understand that there is a new philosophy to throw template with the code, the tools aren't unfortunately ready for it. Especially IDE support is crucial for me.

One issue is keeping your template and your JS "in-sync". For example, if you're looking at {this.something} in your template then you need to context switch (find the corresponding JS file) to understand what's actually driving that data.

There is no issue to keep them sync, we use TypeScript! It throws an error if I compile this with webpack and there are missing or incorrect variables, e.g {this.foo} in component which has no foo defined. I have not had this pleasure ever, I used Django and it never statically checked templates until you start to find errors in production.

Edit I get these nice errors if I typo something in my template:

ERROR in ./app.ts
(Line: 21, Char: 168) Property 'rexsults' does not exist on type '{ results: string[]; }'.

A bigger issue is something like looping. JSX has no concept of looping: instead you just use plain JavaScript.

This is not an issue, I just tried:

<div>
    <span class="msg">Yello {this.props.ticks}</span>
    <Testi />
    {this.state.results.map(
        function(result) {
            return <div>Test</div>
        })}
</div>

Worked just fine in own file, when inlining.

I'm forking this to a new more streamlined loader for this specific purpose, which works with only syntax React.jsxFile consider this closed.

I forgot, is it okay for me to scrub your name from LICENSE, in my fork ts-jsxfile-loader? This contains a still some of your wordings etc. I can put both names in there too.

I honestly have no clue what the normal procedure for that is. IANAL. Some googling turned this up: http://programmers.stackexchange.com/questions/157968/how-to-manage-a-copyright-notice-in-an-open-source-project