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

add appendTsxSuffixTo option #581

Merged
merged 16 commits into from
Jul 15, 2017
55 changes: 54 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,8 @@ of your code.
To be used in concert with the `allowJs` compiler option. If your entry file is JS then you'll need to set this option to true. Please note that this is rather unusual and will generally not be necessary when using `allowJs`.

#### appendTsSuffixTo *(RegExp[]) (default=[])*
A list of regular expressions to be matched against filename. If filename matches one of the regular expressions, a `.ts` suffix will be appended to that filename.
#### appendTsxSuffixTo *(RegExp[]) (default=[])*
A list of regular expressions to be matched against filename. If filename matches one of the regular expressions, a `.ts` or `.tsx` suffix will be appended to that filename.

This is useful for `*.vue` [file format](https://vuejs.org/v2/guide/single-file-components.html) for now. (Probably will benefit from the new single file format in the future.)

Expand Down Expand Up @@ -274,6 +275,58 @@ export default {
</script>
```

We can use .vuex to handle tsx (set `jsx` option in `tsconfig.json` to `preserve` to let babel handle jsx):

webpack.config.js:

```javascript
module.exports = {
entry: './index.vue',
output: { filename: 'bundle.js' },
resolve: {
extensions: ['.ts', '.tsx', '.vue', '.vuex']
},
module: {
rules: [
{ test: /\.vue$/, loader: 'vue-loader' },
{ test: /\.vuex$/, loader: 'vue-loader',
options: {
loaders: {
tsx: 'babel-loader!ts-loader',
}
}
},
{ test: /\.ts$/, loader: 'ts-loader', options: { appendTsSuffixTo: [/\.vue$/], appendTsxSuffixTo: [/\.vuex$/] } }
{ test: /\.tsx$/, loader: 'babel-loader!ts-loader', options: { appendTsSuffixTo: [/\.vue$/], appendTsxSuffixTo: [/\.vuex$/] } }
]
}
}
```

tsconfig.json

```json
{
"compilerOptions": {
"jsx": "preserve"
}
}
```

index.vue

```vue
<script lang="tsx">
export default {
functional: true,
render(h, c) {
return (<div>Content</div>);
}
}
</script>
```


### `LoaderOptionsPlugin`

[There's a known "gotcha"](https://github.com/TypeStrong/ts-loader/issues/283) if you are using webpack 2 with the `LoaderOptionsPlugin`. If you are faced with the `Cannot read property 'unsafeCache' of undefined` error then you probably need to supply a `resolve` object as below: (Thanks @jeffijoe!)
Expand Down
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ function loader(this: interfaces.Webpack, contents: string) {
}

const rawFilePath = path.normalize(this.resourcePath);
const filePath = utils.appendTsSuffixIfMatch(options.appendTsSuffixTo, rawFilePath);
let filePath = utils.appendTsSuffixIfMatch(options.appendTsSuffixTo, rawFilePath);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Come the first uses rawFilePath and the second uses filePath?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So why this:

let filePath = utils.appendTsSuffixIfMatch(options.appendTsSuffixTo, rawFilePath);
filePath = utils.appendTsxSuffixIfMatch(options.appendTsxSuffixTo, filePath); // shouldn't this be rawFilePath too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only different is when filename is matched by both options.appendTsxSuffixTo and options.appendTsSuffixTo.
In that case, if both part use rawFilePath then .tsx append will overwrite .ts, but i think it should become .ts.tsx so user may know that their regexes has collision.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right - thanks for that. Is it likely that people will use appendTsxSuffixTo and appendTsSuffixTo together? Or is it an either / or thing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They can use it together if they want (declare at a single place and then reuse for example), i can code it more generic but after consider backward-compability i chose this method.

filePath = utils.appendTsxSuffixIfMatch(options.appendTsxSuffixTo, filePath);
const fileVersion = updateFileInCache(filePath, contents, instance);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be appendSuffixesIfMatch also please? Thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry i already code that but forgot to save...


const { outputText, sourceMapText } = options.transpileOnly
Expand Down Expand Up @@ -83,6 +84,7 @@ function getLoaderOptions(loader: interfaces.Webpack) {
visualStudioErrorFormat: false,
compilerOptions: {},
appendTsSuffixTo: [],
appendTsxSuffixTo: [],
transformers: {},
entryFileIsJs: false,
happyPackMode: false,
Expand Down
2 changes: 1 addition & 1 deletion src/instances.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export function getTypeScriptInstance(
modifiedFiles: null,
};

const servicesHost = makeServicesHost(scriptRegex, log, loader, instance, loaderOptions.appendTsSuffixTo);
const servicesHost = makeServicesHost(scriptRegex, log, loader, instance, loaderOptions.appendTsSuffixTo, loaderOptions.appendTsxSuffixTo);
instance.languageService = compiler.createLanguageService(servicesHost, compiler.createDocumentRegistry());

loader._compiler.plugin("after-compile", afterCompile(instance, configFilePath));
Expand Down
1 change: 1 addition & 0 deletions src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ export interface LoaderOptions {
visualStudioErrorFormat: boolean;
compilerOptions: typescript.CompilerOptions;
appendTsSuffixTo: RegExp[];
appendTsxSuffixTo: RegExp[];
entryFileIsJs: boolean;
happyPackMode: boolean;
getCustomTransformers?(): typescript.CustomTransformers | undefined;
Expand Down
12 changes: 8 additions & 4 deletions src/servicesHost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ function makeServicesHost(
log: logger.Logger,
loader: interfaces.Webpack,
instance: interfaces.TSInstance,
appendTsSuffixTo: RegExp[]
appendTsSuffixTo: RegExp[],
appendTsxSuffixTo: RegExp[]
) {
const { compiler, compilerOptions, files } = instance;

Expand Down Expand Up @@ -77,7 +78,7 @@ function makeServicesHost(
log: log.log,
resolveModuleNames: (moduleNames: string[], containingFile: string) =>
resolveModuleNames(
resolveSync, moduleResolutionHost, appendTsSuffixTo, scriptRegex, instance,
resolveSync, moduleResolutionHost, appendTsSuffixTo, appendTsxSuffixTo, scriptRegex, instance,
moduleNames, containingFile),
getCustomTransformers: () => instance.transformers
};
Expand All @@ -87,13 +88,14 @@ function resolveModuleNames(
resolveSync: interfaces.ResolveSync,
moduleResolutionHost: interfaces.ModuleResolutionHost,
appendTsSuffixTo: RegExp[],
appendTsxSuffixTo: RegExp[],
scriptRegex: RegExp,
instance: interfaces.TSInstance,
moduleNames: string[],
containingFile: string
) {
const resolvedModules = moduleNames.map(moduleName =>
resolveModuleName(resolveSync, moduleResolutionHost, appendTsSuffixTo, scriptRegex, instance,
resolveModuleName(resolveSync, moduleResolutionHost, appendTsSuffixTo, appendTsxSuffixTo, scriptRegex, instance,
moduleName, containingFile)
);

Expand All @@ -106,6 +108,7 @@ function resolveModuleName(
resolveSync: interfaces.ResolveSync,
moduleResolutionHost: interfaces.ModuleResolutionHost,
appendTsSuffixTo: RegExp[],
appendTsxSuffixTo: RegExp[],
scriptRegex: RegExp,
instance: interfaces.TSInstance,

Expand All @@ -118,7 +121,8 @@ function resolveModuleName(

try {
const originalFileName = resolveSync(undefined, path.normalize(path.dirname(containingFile)), moduleName);
const resolvedFileName = utils.appendTsSuffixIfMatch(appendTsSuffixTo, originalFileName);
let resolvedFileName = utils.appendTsSuffixIfMatch(appendTsSuffixTo, originalFileName);
resolvedFileName = utils.appendTsxSuffixIfMatch(appendTsxSuffixTo, resolvedFileName);

if (resolvedFileName.match(scriptRegex)) {
resolutionResult = { resolvedFileName, originalFileName };
Expand Down
11 changes: 11 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,17 @@ export function appendTsSuffixIfMatch(patterns: RegExp[], path: string): string
return path;
}

export function appendTsxSuffixIfMatch(patterns: RegExp[], path: string): string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having pondered I think it might make for nicer code to combine appendTsSuffixIfMatch and appendTsxSuffixIfMatch.

Here's my reasoning:

Wherever appendTsSuffixIfMatch is called in the code, the output is then passed into the call to appendTsxSuffixIfMatch. They are never called in isolation and are intended to be used together.

That being the case, I think it might make sense to have an appendTsTsxSuffixIfMatch method which internally does the appendTsSuffixIfMatch and appendTsxSuffixIfMatch operations, feeding the output of the first into the second. This should result in clearer code at the call sites. It may also allow for some genericisation for the underlying methods (whether or not that makes sense I don't know)

Is that okay with you? I'm quite focused on keeping the source of ts-loader as comprehensible as possible to ease contributions by others.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've just commit.

if (patterns.length > 0) {
for (let regexp of patterns) {
if (path.match(regexp)) {
return path + '.tsx';
}
}
}
return path;
}

/**
* Recursively collect all possible dependants of passed file
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ Module build failed: Error: Typescript emitted no output for node_modules/a/inde
You should not need to recompile .ts files in node_modules.
Please contact the package author to advise them to use --declaration --outDir.
More https://github.com/Microsoft/TypeScript/issues/12358
at Object.loader (dist/index.js:31:15)
at Object.loader (dist/index.js:33:15)
@ ./.test/nodeModulesMeaningfulErrorWhenImportingTs/app.ts 2:8-20
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ Module build failed: Error: Typescript emitted no output for node_modules/a/inde
You should not need to recompile .ts files in node_modules.
Please contact the package author to advise them to use --declaration --outDir.
More https://github.com/Microsoft/TypeScript/issues/12358
at Object.loader (dist/index.js:31:15)
at Object.loader (dist/index.js:33:15)
@ ./.test/nodeModulesMeaningfulErrorWhenImportingTs/app.ts 2:8-20
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ Module build failed: Error: Typescript emitted no output for node_modules\a\inde
You should not need to recompile .ts files in node_modules.
Please contact the package author to advise them to use --declaration --outDir.
More https://github.com/Microsoft/TypeScript/issues/12358
at Object.loader (dist\index.js:32:15)
at Object.loader (dist\index.js:33:15)
@ ./.test/nodeModulesMeaningfulErrorWhenImportingTs/app.ts 3:8-20
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ Module build failed: Error: Typescript emitted no output for node_modules\a\inde
You should not need to recompile .ts files in node_modules.
Please contact the package author to advise them to use --declaration --outDir.
More https://github.com/Microsoft/TypeScript/issues/12358
at Object.loader (dist\index.js:32:15)
at Object.loader (dist\index.js:33:15)
@ ./.test/nodeModulesMeaningfulErrorWhenImportingTs/app.ts 3:8-20
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ Module build failed: Error: Typescript emitted no output for node_modules\a\inde
You should not need to recompile .ts files in node_modules.
Please contact the package author to advise them to use --declaration --outDir.
More https://github.com/Microsoft/TypeScript/issues/12358
at Object.loader (dist\index.js:31:15)
at Object.loader (dist\index.js:32:15)
@ ./.test/nodeModulesMeaningfulErrorWhenImportingTs/app.ts 3:8-20