Skip to content
This repository has been archived by the owner on Dec 1, 2019. It is now read-only.

Commit

Permalink
feat: initial impl of PathsPlugin, refs #156
Browse files Browse the repository at this point in the history
  • Loading branch information
s-panferov committed Jun 25, 2016
1 parent e907105 commit da70ef1
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 20 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"homepage": "https://github.com/s-panferov/awesome-typescript-loader",
"dependencies": {
"colors": "^1.1.2",
"enhanced-resolve": "^2.2.2",
"loader-utils": "^0.2.6",
"lodash": "^4.13.1",
"object-assign": "^4.1.0",
Expand All @@ -56,7 +57,7 @@
"sinon": "^1.17.4",
"temp": "^0.8.3",
"tslint": "3.11.0-dev.0",
"typescript": "^1.9.0-dev.20160620-1.0",
"typescript": "^1.9.0-dev.20160625-1.0",
"webpack": "2.1.0-beta.4"
}
}
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { findCompiledModule, cache } from './cache';
import * as helpers from './helpers';
import { isTypeDeclaration } from './deps';
import { QueryOptions, IWebPack, ensureInstance, ICompilerInstance } from './instance';
import { PathsPlugin } from './paths-plugin';

let loaderUtils = require('loader-utils');

Expand Down Expand Up @@ -221,5 +222,6 @@ class ForkCheckerPlugin {
}

(loader as any).ForkCheckerPlugin = ForkCheckerPlugin;
(loader as any).TsConfigPathsPlugin = PathsPlugin;

module.exports = loader;
43 changes: 25 additions & 18 deletions src/instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,23 +138,8 @@ export function ensureInstance(webpack: IWebPack, query: QueryOptions, instanceN
return exInstance;
}

let compilerPath = query.compiler || 'typescript';

let tsImpl: typeof ts;
let tsImplPath: string;
try {
tsImplPath = require.resolve(compilerPath);
tsImpl = require(tsImplPath);
} catch (e) {
console.error(e);
console.error(COMPILER_ERROR);
process.exit(1);
}

let compilerInfo: ICompilerInfo = {
compilerPath,
tsImpl,
};
let compilerInfo = setupTs(query.compiler);
let { tsImpl } = compilerInfo;

let { configFilePath, compilerConfig, loaderConfig } = readConfigFile(process.cwd(), query, tsImpl);

Expand Down Expand Up @@ -219,6 +204,28 @@ export function ensureInstance(webpack: IWebPack, query: QueryOptions, instanceN
};
}

export function setupTs(compiler: string): ICompilerInfo {
let compilerPath = compiler || 'typescript';

let tsImpl: typeof ts;
let tsImplPath: string;
try {
tsImplPath = require.resolve(compilerPath);
tsImpl = require(tsImplPath);
} catch (e) {
console.error(e);
console.error(COMPILER_ERROR);
process.exit(1);
}

let compilerInfo: ICompilerInfo = {
compilerPath,
tsImpl,
};

return compilerInfo;
}

function setupCache(loaderConfig: LoaderConfig, tsImpl: typeof ts, webpack: IWebPack, babelImpl: any) {
let cacheIdentifier = null;
if (loaderConfig.useCache) {
Expand Down Expand Up @@ -292,7 +299,7 @@ export interface Configs {
loaderConfig: LoaderConfig;
}

function readConfigFile(baseDir: string, query: QueryOptions, tsImpl: typeof ts): Configs {
export function readConfigFile(baseDir: string, query: QueryOptions, tsImpl: typeof ts): Configs {
let configFilePath: string;
if (query.tsconfig && query.tsconfig.match(/\.json$/)) {
configFilePath = query.tsconfig;
Expand Down
158 changes: 158 additions & 0 deletions src/paths-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { setupTs, readConfigFile, LoaderConfig } from './instance';
import * as path from 'path';

const ModulesInRootPlugin: new (a: string, b: string, c: string) => ResolverPlugin
= require('enhanced-resolve/lib/ModulesInRootPlugin');

const createInnerCallback: CreateInnerCallback = require('enhanced-resolve/lib/createInnerCallback');
const getInnerRequest: getInnerRequest = require('enhanced-resolve/lib/getInnerRequest');

type CreateInnerCallback = (callback: Callback, options: Callback, message?: string, messageOptional?: string) => Callback;
type getInnerRequest = (resolver: Resolver, request: Request) => string;

export interface Request {
request?: Request;
relativePath: string;
}

export interface Callback {
(err?: Error, result?: any): void;

log?: any;
stack?: any;
missing?: any;
}

type ResolverCallback = (request: Request, callback: Callback) => void;

interface ResolverPlugin {
apply(resolver: Resolver): void;
}

export interface Resolver {
apply(plugin: ResolverPlugin): void;
plugin(source: string, cb: ResolverCallback);
doResolve(target: string, req: Request, desc: string, Callback);
join(relativePath: string, innerRequest: Request): Request;
}

interface Mapping {
onlyModule: boolean;
alias: string;
aliasPattern: RegExp;
target: string;
}

function escapeRegExp(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}

export class PathsPlugin implements ResolverPlugin {
source: string;
target: string;
ts: typeof ts;
configFilePath: string;
options: ts.CompilerOptions;

baseUrl: string;
mappings: Mapping[];
absoluteBaseUrl: string;

constructor(config: LoaderConfig & ts.CompilerOptions = {} as any) {
this.source = 'described-resolve';
this.target = 'resolve';

this.ts = setupTs(config.compiler).tsImpl;

let { configFilePath, compilerConfig } = readConfigFile(process.cwd(), config, this.ts);
this.options = compilerConfig.options;
this.configFilePath = configFilePath;

this.baseUrl = this.options.baseUrl;
this.absoluteBaseUrl = path.resolve(
path.dirname(this.configFilePath),
this.baseUrl
);

console.log(this.configFilePath, this.absoluteBaseUrl)

this.mappings = [];
let paths = this.options.paths || {};
Object.keys(paths).forEach(alias => {
let onlyModule = alias.indexOf('*') === -1;
let excapedAlias = escapeRegExp(alias);
let targets = paths[alias];
targets.forEach(target => {
let aliasPattern: RegExp;
if (onlyModule) {
aliasPattern = new RegExp(`^${excapedAlias}$`);
} else {
let withStarCapturing = excapedAlias.replace('\\*', '(.*)');
aliasPattern = new RegExp(`^${withStarCapturing}`);
}

this.mappings.push({
onlyModule,
alias,
aliasPattern,
target: target
});
});
});
}

apply(resolver: Resolver) {
let { baseUrl, mappings } = this;

if (baseUrl) {
resolver.apply(new ModulesInRootPlugin("module", this.absoluteBaseUrl, "resolve"));
}

mappings.forEach(mapping => {
resolver.plugin(this.source, this.createPlugin(resolver, mapping))
});
}

createPlugin(resolver: Resolver, mapping: Mapping) {
return (request, callback) => {
let innerRequest = getInnerRequest(resolver, request);
if (!innerRequest) {
return callback();
}

let match = innerRequest.match(mapping.aliasPattern);
if (!match) {
return callback();
}

let newRequestStr = mapping.target;
if (!mapping.onlyModule) {
newRequestStr = newRequestStr.replace('*', match[1]);
}

let newRequest: Request = Object.assign({}, request, {
request: newRequestStr
});

console.log("aliased'" + innerRequest + "': '" + mapping.alias + "' to '" + newRequestStr + "'");

return resolver.doResolve(
this.target,
newRequest,
"aliased with mapping '" + innerRequest + "': '" + mapping.alias + "' to '" + newRequestStr + "'",
createInnerCallback(
function(err, result) {
console.log(err, result);
if (arguments.length > 0) {
return callback(err, result);
}

// don't allow other aliasing or raw request
callback(null, null);
},
callback
)
);
};
}
}
3 changes: 2 additions & 1 deletion src/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"sourceMap": true,
"removeComments": true,
"preserveConstEnums": true,
"suppressImplicitAnyIndexErrors": true
"suppressImplicitAnyIndexErrors": true,
"lib": [ "es6", "dom" ]
},
"compileOnSave": false,
"exclude": [
Expand Down

0 comments on commit da70ef1

Please sign in to comment.