Skip to content

Suggestion: custom loaders for webpack integration. #12828

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
aj0strow opened this issue Dec 10, 2016 · 9 comments
Closed

Suggestion: custom loaders for webpack integration. #12828

aj0strow opened this issue Dec 10, 2016 · 9 comments
Labels
Suggestion An idea for TypeScript Too Complex An issue which adding support for may be too complex for the value it adds

Comments

@aj0strow
Copy link

Please see my prior issue. (#11975)

Scenario

I have a pretty normal looking react project layout.

project/
├── node_modules
├── src
│   ├── modules
│   └── components
├── webpack.config.js
└── tsconfig.json

I have the following goals.

  • Import commonjs modules from /node_modules.
  • Don't import modules from $HOME/node_modules.
  • Import es6 modules from /src by name (not by relative path)
  • Import css modules and generate default typescript export on the fly.
  • Import react components with inline HTML in JSX syntax.
  • Import typings from /typings.
  • Import typings from /node_modules/@types.
  • Build project with webpack.

It's unclear how to accomplish my entire list. Webpack works great for JavaScript, but the issue is TypeScript can never find anything. I want static typing but it's too difficult to figure out. Instead, I end up with a hybrid code base.

Solution

See Jest transform for inspiration.

It would be nice if TypeScript allowed user defined resolution and file reading functions. I'd imagine the types could look like this:

resolveModule(absolutePath: string, moduleName: string): Promise<FileDescriptor>
readFile(absolutePath: string): Promise<NodeBuffer, SourceMap?>

I could provide nodejs modules that adhere to the interface.

{
    compilerOptions: {
        resolveModule: "./config/typescript/resolve.js",
        readFile: "./config/typescript/read.js",
    },
}

It would open up a world of possibilities for developers without needing constant changes to TypeScript core. The most popular strategies (like commonjs and typescript) could be bundled as a string constant instead of a path to code.

// tsconfig.json

{
    compilerOptions: {
        resolveModule: "commonjs",
        readFile: "typescript",
    },
}

If I want to do unusual things, like import image files as type string or parse CSS files for safe css module access, I should be allowed. The concept is similar to webpack loaders.

It's simple and configurable. It doesn't lock developers into using specific file extensions, or module resolution strategies, or frameworks, or build systems. TypeScript should just check types, and let developers provide modules and definitions as needed.

@aluanhaddad
Copy link
Contributor

aluanhaddad commented Dec 11, 2016

The only thing that I believe is currently problematic is

Don't import modules from $HOME/node_modules

I often get types that are incidentally on disk in parent directories.

For the remaining items this does the trick:

tsconfig.json

{
    "compilerOptions": {
        "moduleResolution": "node",
        "target": "es2015",
        "module": "es2015",
        "jsx": "react",
        "allowSyntheticDefaultImports": true,
        "baseUrl": "src",
        "paths": {
            "*":[
                "modules/*"
            ]
        }
    }
}

globals.d.ts

declare module '*.css' {
    const css: React.CSSProperties;
    export default css;
}

src/modules/main.tsx

import $ from 'jquery'; // typings/globals/jquery/index.d.ts
import * as React from 'react'; // node_modules/@types/react/index.d.ts
import {render} from 'react-dom'; // node_modules/@types/react/index.d.ts
import square from 'square'; // src/modules/square.ts
import style from 'main.css'; // globals.d.ts

export default () => render(
    <div style={style}>
        {square(4)}
    </div>,
    $('#app')[0]
); 

@aluanhaddad
Copy link
Contributor

Sorry I think I misunderstood the issue. Are you asking for the language service to parse the CSS files?
I think

resolveModule: "./config/typescript/resolve.js",

would be immensely valuable but that

readFile: "typescript",

is not a good idea.

@aj0strow
Copy link
Author

Thanks for the tsconfig.json example. I updated mine to what you suggested. I had it working except for importing folders using index.ts or index.tsx which is now fixed.

With respect to this issue, I guess it's two separate parts.

  1. Complete control of mapping module name to absolute path. It could "include batteries" for normal setups.

  2. Some way to preprocess non-typescript imports to either lookup or generate .d.ts. It's a general solution for typings vs @types and would allow cool stuff like importing assets safely in webpack, or even loading file formats that don't exist yet. I realize that could be a bit out of scope.

I hope this makes sense.

@aluanhaddad
Copy link
Contributor

aluanhaddad commented Dec 12, 2016

It makes sense but I'm not sure if there is any reason to couple the two things because given the first capability, the second is just a matter of generating declaration files to describe the shape of these files.

The first definitely seems in scope but the second is not currently a TypeScript concern and does not need to become one for this to work. Ultimately, it is some other tool, e.g. a Webpack plugin that would actually provide the runtime values for these assets. That is perfect, the TypeScript compiler doesn't care about that - all it needs is a type declaration.

@aj0strow
Copy link
Author

Ultimately, it is some other tool, e.g. a Webpack plugin that would actually provide the runtime values for these assets. That is perfect, the TypeScript compiler doesn't care about that - all it needs is a type declaration.

I think we're in agreement.

For sure, TypeScript does not need to know css. It just needs to allow configurable function to map a non-TypeScript import to a real or virtual .d.ts file. Webpack will actually load the css, but TypeScript needs to know the definition of the import so it can find errors in the .tsx code.

I'm ignorant in TypeScript compiler internals, but in theory it works the same way as a JavaScript file import, where you need to map a non-TypeScript (in this case JavaScript) file to the corresponding definition file. It's just a bit more general.

@aluanhaddad
Copy link
Contributor

Technically this is not quite right since TypeScript handles JavaScript code just fine and infers tons of information from and can also transpile it directly. I think they key point is pluggable resolvers for real or virtual module identifiers.

@aj0strow
Copy link
Author

Yeah that's the main point. What do you think?

@RyanCavanaugh RyanCavanaugh added the Needs Investigation This issue needs a team member to investigate its status. label May 24, 2017
@barbu110
Copy link

Anything on this yet?

@RyanCavanaugh RyanCavanaugh added Suggestion An idea for TypeScript Too Complex An issue which adding support for may be too complex for the value it adds and removed Needs Investigation This issue needs a team member to investigate its status. labels Sep 18, 2019
@RyanCavanaugh
Copy link
Member

We already offer the CompilerHost API wherein you can customize file I/O, module resolution, and other behavior. A sufficiently complex setup that can't be solved with path mapping can self-host the compiler with their own CompilerHost and do pretty much anything. Loading arbitrary JS from the config file to do this as part of regular tsc isn't something we'd want to enable for mainline scenarios.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Suggestion An idea for TypeScript Too Complex An issue which adding support for may be too complex for the value it adds
Projects
None yet
Development

No branches or pull requests

4 participants