Skip to content
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

Importing modules with loaders extension #10988

Closed
wclr opened this issue Sep 19, 2016 · 24 comments
Closed

Importing modules with loaders extension #10988

wclr opened this issue Sep 19, 2016 · 24 comments
Labels
Declined The issue was declined as something which matches the TypeScript vision Suggestion An idea for TypeScript

Comments

@wclr
Copy link

wclr commented Sep 19, 2016

Using TS 2.1dev

https://github.com/Microsoft/TypeScript/wiki/What's-new-in-TypeScript#wildcard-character-in-module-names

Say in may code I have

import something from 'loader!./something'

In ts 2 it I can do

declare module "loader!*" {
    const value: any;
    export default value;
}

But is it possible somehow to make this import behave like there is just no loader! part so it would import just exported from particular './something'?

@mhegazy
Copy link
Contributor

mhegazy commented Sep 19, 2016

You can use Path Mapping to achieve this for non-relative modules, but not for relative module imports.

e.g. import something from "loader!otherModule";, can use a path mapping rule like:

"compilerOptions" {
    "paths": {
        "loader!*" : ["*"]
    }
}

This does not work directly for relative paths though.

@mhegazy mhegazy added the Question An issue which isn't directly actionable in code label Sep 19, 2016
@wclr
Copy link
Author

wclr commented Sep 19, 2016

@mhegazy thanks, didn't know about paths mapping it will be very useful. But it seem doesn't work in my case:

tsconifg:

{
  "compilerOptions": {    
   "outDir": "./lib",
   "baseUrl": ".",
   "paths": {
      "loader!*": ["src/*-some"]
   }
  }

and index.ts imports with loader import x from 'loader!./dict':
image

It compiles without errors. But VSCode shows that can not find module and errors. Is it vscode problem or what?

The second thing that "loader!*" : ["*"] will look up relative to baseUrl, not current while where it find import. Are there other suggestions?

@vladima
Copy link
Contributor

vladima commented Sep 19, 2016

you can configure VSCode to use newer versions of TypeScript

@wclr
Copy link
Author

wclr commented Sep 20, 2016

@vladima right thanks forgot that this test project was not using ts2. Ok IDE problem is gone.

"loader!" : [""] will look up relative to baseUrl, not current while where it find

But as I've said import x from "loader!./module" with "loader!*" : ["*"] in tsconfig will lookup ./module relative to baseUrl, not importing parent. Is is possible do anything about it?

@Ciantic
Copy link

Ciantic commented Sep 20, 2016

Is this related, I'm trying to replace babel but default imports are causing errors:

e.g.

import something from "somwhere";
something();

Transpiles in target: es5, module: commonjs to seemingly incorrect stuff:

var something = require("somewhere");
something.default();

Babel seems to do this correctly:

var something = require("somewhere");
something();

Since there is no default in the somewhere it fails.

@mhegazy
Copy link
Contributor

mhegazy commented Sep 20, 2016

But as I've said import x from "loader!./module" with "loader!" : [""] in tsconfig will lookup ./module relative to baseUrl, not importing parent. Is is possible do anything about it?

patterns in path mapping are always relative to baseURL.

@mhegazy
Copy link
Contributor

mhegazy commented Sep 20, 2016

TypeScript emits the modules as per the ES6 spec. the ES6 spec stipulates that a module have a default property, and that is what the emit looks like.
consider using import something = require("somewhere"); instead of import something from "somewhere"; for modules that do not support ES6.

@mhegazy mhegazy closed this as completed Sep 20, 2016
@wclr
Copy link
Author

wclr commented Sep 21, 2016

@mhegazy

consider using import something = require("somewhere"); instead of import something from "somewhere"; for modules that do not support ES6.

My OP was not about ES6 formats or something but about import format, and about importing relative modules, with pointing to the loader (this is webpack loader):

import something from 'loader!./something'

but also I would like this module to be personally defined not using wilcard
that measn that I can have multiple of this :

import something from 'loader!./something'

something. //of some specifc typing, not defined using wilcard 

in different dirs, I wan't them to import different things, but it seem I can not do do it, only using wilcard:

// this allows to specify ONE typing for all such imports, but I need different
declare module "loader!*" {
    const value: any;
    export default value;
}

Did I manage to explain myself?

@mhegazy
Copy link
Contributor

mhegazy commented Sep 21, 2016

yes. but that is not supported at the moment.

what is supported is 1. path mapping for non-relative module imports, or 2. wildcard module declarations globally.

the closest request i could think of is #10649.

@wclr
Copy link
Author

wclr commented Sep 21, 2016

I think probably relative modules loader!./something should be resolved relative importing module, not baseUrl, because currenlty it is the same as loader!something.

will break anything?

@kitsonk
Copy link
Contributor

kitsonk commented Sep 21, 2016

should be resolved relative importing module

This assumes TypeScript has to have knowledge of all the different loader behaviours of module resolution. I think it is reasonable that once you have a MID that passes through some sort of loader plugin, you cannot expect that TypeScript would be able to replicate all the possible resolution logic out there. In those cases, you will have to explicitly type the modules in your global typing definitions.

@wclr
Copy link
Author

wclr commented Sep 21, 2016

In those cases, you will have to explicitly type the modules in your global typing definitions.

That is what I would like to do actually, but seem not have this ability currently. So that I have in some module:

import something from 'loader!./something'

And TS would not error, but just load custom definitions for this particular module.

@kitsonk
Copy link
Contributor

kitsonk commented Sep 21, 2016

in a global definition file:

declare module 'loader!./something' {
    import something from 'typings/path/to/something';
    export default something;
}

@wclr
Copy link
Author

wclr commented Sep 21, 2016

in a global definition file:

The thing is that I have many modules with the same line (each imports its own version of ./something relative to itself)

import something from 'loader!./something' 

So single global definition won't work

@mhegazy
Copy link
Contributor

mhegazy commented Sep 21, 2016

The assumption that you would use a loader extension if you have a non-code resource, e.g. a text file, a css, image or json. if you have a source file, either .ts or a just a .js, you would not use a loader extension.
in this mindset, a loader extension, e.g. text! will always return you a string, regardless of what file it takes. son declare module "text!*" ...works.

I am not completely sure i understand why you use a loader extension, but turn around and get it from the loader, and why this can not be done as a global setting. but regardless, the wild-card module augmentation was not meant for this scenario.

The only other thing that closely resembles the request, is the path mapping module resolution. again this does not support relative path, as it is not what most loaders like system, or amd do.

it would be educational for me if you can elaborate on what your loader is, how you use it and why other patterns would work. it would also be useful to know how your loader resolves these modules at runtime, and if your loader has an extension pattern, like webpack or browserify, and if so, why not author a TS extension like tsify, or awesome-typescript-loader to do the custom module resolution, which is fully customizatable if you are using the compiler API instead of the command line compilation.

@wclr
Copy link
Author

wclr commented Sep 21, 2016

@mhegazy Ok I will try to explain. hope will make myself clear)

I've made a webpack loader for implementing language packs. While build it creates language packs (bundles) for each produced built chunk, each such language bundle contains only specific language messages, and those bundle are loaded by application on dynamically on-demand, when specific language messages are accessed.

it works this way, we have the followng structure for the component:

/MyComponent
  /locale
    /en.ts
    /es.ts
   /index.ts
  component.ts

/locale/index.ts:

// this is dict type, defines structure of the messages
export type Dict = {
  myMessage: string
  ...
}

export default (lang: string) : Promise<Dict> => {
  return Promise.resolve({})  // return dummy promise just to enable typings
} 

Each language file, for example /locale/en.ts

import { Dict } from '.'

let dict: Dict = {
  myMessage: 'This this crazy message in english',
  ...
}

export default dict

inside component.ts:

import locale from './locale'

// usage of imported `locale` is following:
// `locale` here is a function that takes language and returns promise
// as you can see because of proper typings inside the code 
// I've have full access to the structure of the messages
// messages for requested language (if not loaded yet) 
// will be loaded only after first call of:
locale('en').then((dict) => {
  dict.myMessage // <- access to messages
})

For the purpose of dynamic language loading /locale/index.ts in this case does not import any of specific languages files, it actually returns a function that returns a dummy promise, just to provide proper typing while development.

All the "magic" is done inside webpack loader, that actually even doesn't read the content of locale/index.ts file, but creates a specific source for dynamic importing and ensures building of language separate bundles per chunk - that is why we need this loader. While development we need this content of locale/index.ts and while webpack build we do actually not.

Currently I just tune webpack.config to use needed loader for when loading of ../locale/index.ts modules and it works. Why I asked this question is that I wanted to be able to point to the loader locally inside the module.

import locale from 'loader!./locale'

Well I just though that there can be need of this potentially, At least webpack and systemjs allow to point to loaders inside the import statements.

The assumption that you would use a loader extension if you have a non-code resource, e.g. a text file, a css,

So, as you see, this assumption I believe not always completely valid.

@mhegazy
Copy link
Contributor

mhegazy commented Sep 21, 2016

so are you using a webpack loader to build like awesome-typescript-loader?

@wclr
Copy link
Author

wclr commented Sep 21, 2016

I'm using ts and my lp loader:

{ test: /locale(\\|\/)index.ts/, loader: 'lp!ts' },
{ test: /\.ts$/, loader: 'ts', exclude: /locale(\\|\/)index/ },

@mhegazy
Copy link
Contributor

mhegazy commented Sep 21, 2016

no i mean how are you calling the compiler, pardon my ignorance.

@wclr
Copy link
Author

wclr commented Sep 21, 2016

Compiler is called by webpack, see loader: 'ts'
And as ts loader I use awesome-typescript-loader

resolveLoader: <any>{
    extensions: ['.ts', '.js'],
    alias: {
      'ts': 'awesome-typescript-loader' 
    },

@mhegazy
Copy link
Contributor

mhegazy commented Sep 21, 2016

well.. the compiler will call awesome-typescript-loader to resolve every module name it sees. so this would be a possible point of extension where you can translate loader!./foo to ./foo. from an API point of view it should be doable.

@wclr
Copy link
Author

wclr commented Sep 21, 2016

Webpack has no problems with loader!./foo, tsc and IDE has problems.

@mhegazy mhegazy reopened this Sep 22, 2016
@mhegazy mhegazy added Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. and removed Question An issue which isn't directly actionable in code labels Sep 22, 2016
@mhegazy
Copy link
Contributor

mhegazy commented Sep 22, 2016

We will need proposal for this issue.

@RyanCavanaugh RyanCavanaugh added Declined The issue was declined as something which matches the TypeScript vision and removed Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. labels Jun 23, 2021
@RyanCavanaugh
Copy link
Member

Declining due to a lack of demand. Incoming linked issues outline some possible next steps in this area.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Declined The issue was declined as something which matches the TypeScript vision Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

6 participants