diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000..8f08319 --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,16 @@ +import * as Loader from './src/Loader'; +import * as options from './src/options'; +declare function loader(source: any, sourcemap: any): void; +declare module loader { + type ReplaceResult = Loader.ReplaceResult; + type LoaderCodeGen = Loader.LoaderCodeGen; + type RouterLoaderOptions = options.RouterLoaderOptions; + type RouteResourceOptions = options.RouteResourceOptions; + /** + * Add a code generator that can be used in the 'loader' option. + * @param name + * @param codeGen + */ + function setCodeGen(name: string, codeGen: Loader.LoaderCodeGen): void; +} +export = loader; diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..d11dfdb --- /dev/null +++ b/dist/index.js @@ -0,0 +1,44 @@ +"use strict"; +var Loader = require('./src/Loader'); +var builtin_codegens_1 = require('./src/builtin_codegens'); +function loader(source, sourcemap) { + var _this = this; + this.cacheable && this.cacheable(); + this.async(); + var l = new Loader.Loader(this); + l.replace(source) + .then(function (results) { + if (results) { + results.forEach(function (result) { + source = result.source; + if (result.debug) { + var d = [ + '================================== ng-router-loader ==================================', + ("Importer: " + _this.resourcePath), + ("Raw Request: " + result.match), + ("Replacement: " + result.replacement), + '======================================================================================' + ]; + console.log(d.join('\n')); + } + }); + } + _this.callback(null, source, sourcemap); + }) + .catch(function (err) { return _this.callback(err); }); +} +var loader; +(function (loader) { + /** + * Add a code generator that can be used in the 'loader' option. + * @param name + * @param codeGen + */ + function setCodeGen(name, codeGen) { + Loader.Loader.setCodeGen(name, codeGen); + } + loader.setCodeGen = setCodeGen; +})(loader || (loader = {})); +builtin_codegens_1.BUILT_IN_CODEGENS.forEach(function (cgDef) { return Loader.Loader.setCodeGen(cgDef.name, cgDef.codeGen); }); +module.exports = loader; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/index.js.map b/dist/index.js.map new file mode 100644 index 0000000..f65db8e --- /dev/null +++ b/dist/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";AAAA,IAAY,MAAM,WAAM,cAAc,CAAC,CAAA;AAEvC,iCAAkC,wBAAwB,CAAC,CAAA;AAE3D,gBAAgB,MAAM,EAAE,SAAS;IAAjC,iBA4BC;IA3BC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;IAEnC,IAAI,CAAC,KAAK,EAAE,CAAC;IAEb,IAAM,CAAC,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAElC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;SACd,IAAI,CAAE,UAAA,OAAO;QACZ,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;YACZ,OAAO,CAAC,OAAO,CAAE,UAAA,MAAM;gBACrB,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;gBAEvB,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;oBACjB,IAAM,CAAC,GAAG;wBACR,wFAAwF;wBACxF,mBAAgB,KAAI,CAAC,YAAY,CAAE;wBACnC,mBAAgB,MAAM,CAAC,KAAK,CAAE;wBAC9B,mBAAgB,MAAM,CAAC,WAAW,CAAE;wBACpC,wFAAwF;qBACzF,CAAC;oBACF,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC5B,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;QACD,KAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;IACzC,CAAC,CAAC;SACD,KAAK,CAAE,UAAA,GAAG,IAAI,OAAA,KAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAlB,CAAkB,CAAE,CAAC;AACxC,CAAC;AAED,IAAO,MAAM,CAcZ;AAdD,WAAO,MAAM,EAAC,CAAC;IAMb;;;;OAIG;IACH,oBAA2B,IAAY,EAAE,OAA6B;QACpE,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;IAFe,iBAAU,aAEzB,CAAA;AACH,CAAC,EAdM,MAAM,KAAN,MAAM,QAcZ;AAED,oCAAiB,CAAC,OAAO,CAAE,UAAC,KAAK,IAAK,OAAA,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,EAAnD,CAAmD,CAAE,CAAC;AAE5F,iBAAS,MAAM,CAAC","sourcesContent":["import * as Loader from './src/Loader';\r\nimport * as options from './src/options';\r\nimport { BUILT_IN_CODEGENS } from './src/builtin_codegens';\r\n\r\nfunction loader(source, sourcemap) {\r\n this.cacheable && this.cacheable();\r\n\r\n this.async();\r\n\r\n const l = new Loader.Loader(this);\r\n\r\n l.replace(source)\r\n .then( results => {\r\n if (results) {\r\n results.forEach( result => {\r\n source = result.source;\r\n\r\n if (result.debug) {\r\n const d = [\r\n '================================== ng-router-loader ==================================',\r\n `Importer: ${this.resourcePath}`,\r\n `Raw Request: ${result.match}`,\r\n `Replacement: ${result.replacement}`,\r\n '======================================================================================'\r\n ];\r\n console.log(d.join('\\n'));\r\n }\r\n });\r\n }\r\n this.callback(null, source, sourcemap);\r\n })\r\n .catch( err => this.callback(err) );\r\n}\r\n\r\nmodule loader {\r\n export type ReplaceResult = Loader.ReplaceResult;\r\n export type LoaderCodeGen = Loader.LoaderCodeGen;\r\n export type RouterLoaderOptions = options.RouterLoaderOptions;\r\n export type RouteResourceOptions = options.RouteResourceOptions;\r\n\r\n /**\r\n * Add a code generator that can be used in the 'loader' option.\r\n * @param name\r\n * @param codeGen\r\n */\r\n export function setCodeGen(name: string, codeGen: Loader.LoaderCodeGen) {\r\n Loader.Loader.setCodeGen(name, codeGen);\r\n }\r\n}\r\n\r\nBUILT_IN_CODEGENS.forEach( (cgDef) => Loader.Loader.setCodeGen(cgDef.name, cgDef.codeGen) );\r\n\r\nexport = loader;\r\n"]} \ No newline at end of file diff --git a/dist/src/Loader.d.ts b/dist/src/Loader.d.ts new file mode 100644 index 0000000..cd7cfb0 --- /dev/null +++ b/dist/src/Loader.d.ts @@ -0,0 +1,54 @@ +/// +import { RouterLoaderOptions, RouteResourceOptions } from './options'; +export interface ReplaceResult { + /** + * Debug mode + */ + debug: boolean; + /** + * The resolved path + */ + filePath: string; + /** + * The NgModule property on the resolved module + */ + moduleName: string; + /** + * The RESOURCE query used to resolve the module. + * Note: This is the query defined on the URI used in "loadChildren", not the global query. + */ + resourceQuery: RouteResourceOptions; + /** + * The updated source file. + */ + source: string; + /** + * The content remove from the source file. + */ + match: string; + /** + * The content inserted into the source file. + */ + replacement: string; +} +export declare type LoaderCodeGen = Function & (((file: string, module: string) => string) | ((file: string, module: string, loaderOptions: RouterLoaderOptions) => string) | ((file: string, module: string, loaderOptions: RouterLoaderOptions, resourceOptions: RouteResourceOptions) => string)); +export declare class Loader { + private webpack; + query: RouterLoaderOptions; + constructor(webpack: any); + replace(source: string): Promise<[ReplaceResult] | undefined>; + private resolve(context, resourceUri); + private replaceSource(match, loadChildrenPath); + private normalize(filePath); + private trackSymbolRootDecl(absPath, moduleName); + /** + * Convert a source tree file path into a it's genDir representation + * this only change the path to the file, not the file iteself (i.e: suffix) + * @param absFilePath + * @returns {string} + */ + private sourceTreeToGenDir(absFilePath); + private genDirToSourceTree(absFilePath); + static setCodeGen(name: string, codeGen: LoaderCodeGen): void; + static LOADER_CODEGEN_MAP: Map; +} diff --git a/dist/src/Loader.js b/dist/src/Loader.js new file mode 100644 index 0000000..e9cf264 --- /dev/null +++ b/dist/src/Loader.js @@ -0,0 +1,144 @@ +"use strict"; +var loaderUtils = require('loader-utils'); +var os = require('os'); +var fs = require('fs'); +var path = require('path'); +var options_1 = require('./options'); +var RouteModule_1 = require('./RouteModule'); +var Loader = (function () { + function Loader(webpack) { + this.webpack = webpack; + } + Loader.prototype.replace = function (source) { + //TODO: Move this regex async chaos into AST + var LOAD_CHILDREN_RE = /loadChildren[\s]*:[\s]*['|"](.*?)['|"]/gm; + var promises = []; + var match = LOAD_CHILDREN_RE.exec(source); + while (match) { + var p = this.replaceSource(match[0], match[1]) + .then(function (result) { + source = source.replace(result.match, result.replacement); + return Object.assign(result, { + source: source + }); + }); + promises.push(p); + match = LOAD_CHILDREN_RE.exec(source); + } + if (promises.length > 0) { + return Promise.all(promises); + } + else { + return Promise.resolve(undefined); + } + }; + Loader.prototype.resolve = function (context, resourceUri) { + var _this = this; + return new Promise(function (resolve, reject) { + _this.webpack.resolve(context, resourceUri, function (err, fullPath) { + if (err) { + reject(err); + } + else { + resolve(fullPath); + } + }); + }); + }; + Loader.prototype.replaceSource = function (match, loadChildrenPath) { + var _this = this; + this.query = Object.assign({}, options_1.DEFAULT_OPTIONS, loaderUtils.parseQuery(this.webpack.query)); + var route = new RouteModule_1.RouteDestination(loadChildrenPath, this.webpack.resourcePath, this.query); + var codeGen = Loader.LOADER_CODEGEN_MAP.get(route.options.loader); + if (!codeGen) { + return Promise.reject(new Error("ng-router-loader - Invalid code generator \"" + route.options.loader + "\"")); + } + var context = !route.isRawRelative || !route.isCompiled + ? path.dirname(this.webpack.resourcePath) + : path.dirname(this.genDirToSourceTree(this.webpack.resourcePath)); + return this.resolve(context, route.rawFilePath) + .then(function (filePath) { + var moduleName = route.moduleName; + // update the file path for non-ngfactory files + if (_this.query.aot) { + filePath = _this.sourceTreeToGenDir(filePath); + filePath = filePath.substr(0, filePath.lastIndexOf('.')); + if (route.options.bySymbol) { + filePath = _this.trackSymbolRootDecl(filePath, route.moduleName); + } + filePath = filePath + _this.query.moduleSuffix; + moduleName = moduleName + _this.query.factorySuffix; + } + else { + filePath = filePath.substr(0, filePath.lastIndexOf('.')); + } + filePath = _this.normalize(filePath); + var replacement = codeGen(filePath, moduleName, _this.query, route.options); + return { + debug: typeof _this.query.debug !== 'boolean' ? _this.webpack.debug : _this.query.debug, + filePath: filePath, + moduleName: moduleName, + resourceQuery: route.options, + source: undefined, + match: match, + replacement: replacement + }; + }); + }; + Loader.prototype.normalize = function (filePath) { + var normalizedPath = path.normalize(filePath); + if (os.platform() === 'win32') { + normalizedPath = normalizedPath.replace(/\\/g, '\\\\'); + } + return normalizedPath; + }; + Loader.prototype.trackSymbolRootDecl = function (absPath, moduleName) { + var summarySuffix = '.ngsummary.json'; + if (absPath.endsWith(summarySuffix)) { + var summary_1 = require(absPath); + var symbols = summary_1.symbols + .filter(function (s) { return s.name === moduleName; }) + .filter(function (s) { return summary_1.summaries.some(function (ss) { return ss.metadata.__symbol === s.__symbol; }); }); + var m = symbols[0]; + var filePath = m.filePath.replace(/^(.*)\.d\.ts$/, '$1'); + return this.trackSymbolRootDecl(this.sourceTreeToGenDir(filePath), moduleName); + } + else if (fs.existsSync(absPath + this.query.moduleSuffix + '.ts')) { + return absPath; + } + else { + return this.trackSymbolRootDecl(absPath + summarySuffix, moduleName); + } + }; + /** + * Convert a source tree file path into a it's genDir representation + * this only change the path to the file, not the file iteself (i.e: suffix) + * @param absFilePath + * @returns {string} + */ + Loader.prototype.sourceTreeToGenDir = function (absFilePath) { + if (this.query.genDir && this.query.genDir !== '.') { + var relativeNgModulePath = path.relative(this.query.tsconfigDir, absFilePath); + return path.join(path.resolve(this.query.tsconfigDir, this.query.genDir), relativeNgModulePath); + } + else { + return absFilePath; + } + }; + Loader.prototype.genDirToSourceTree = function (absFilePath) { + if (this.query.genDir && this.query.genDir !== '.') { + var relativeNgModulePath = path.relative(path.resolve(this.query.tsconfigDir, this.query.genDir), absFilePath); + return path.join(this.query.tsconfigDir, relativeNgModulePath); + } + else { + return absFilePath; + } + }; + Loader.setCodeGen = function (name, codeGen) { + Loader.LOADER_CODEGEN_MAP.set(name, codeGen); + }; + Loader.LOADER_CODEGEN_MAP = new Map(); + return Loader; +}()); +exports.Loader = Loader; +//# sourceMappingURL=Loader.js.map \ No newline at end of file diff --git a/dist/src/Loader.js.map b/dist/src/Loader.js.map new file mode 100644 index 0000000..8098c31 --- /dev/null +++ b/dist/src/Loader.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Loader.js","sourceRoot":"","sources":["../../src/Loader.ts"],"names":[],"mappings":"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","sourcesContent":["const loaderUtils = require('loader-utils');\r\nimport * as os from 'os';\r\nimport * as fs from 'fs';\r\nimport * as path from 'path';\r\n\r\nimport { RouterLoaderOptions, RouteResourceOptions, DEFAULT_OPTIONS } from './options';\r\nimport { RouteDestination } from './RouteModule';\r\n\r\nexport interface ReplaceResult {\r\n /**\r\n * Debug mode\r\n */\r\n debug: boolean;\r\n\r\n /**\r\n * The resolved path\r\n */\r\n filePath: string;\r\n\r\n /**\r\n * The NgModule property on the resolved module\r\n */\r\n moduleName: string;\r\n\r\n /**\r\n * The RESOURCE query used to resolve the module.\r\n * Note: This is the query defined on the URI used in \"loadChildren\", not the global query.\r\n */\r\n resourceQuery: RouteResourceOptions;\r\n\r\n /**\r\n * The updated source file.\r\n */\r\n source: string;\r\n\r\n /**\r\n * The content remove from the source file.\r\n */\r\n match: string;\r\n\r\n /**\r\n * The content inserted into the source file.\r\n */\r\n replacement: string\r\n}\r\n\r\nexport type LoaderCodeGen = Function & (\r\n ( (file: string, module: string) => string )\r\n | ( (file: string, module: string, loaderOptions: RouterLoaderOptions) => string )\r\n | ( (file: string, module: string, loaderOptions: RouterLoaderOptions, resourceOptions: RouteResourceOptions) => string )\r\n )\r\n\r\nexport class Loader {\r\n public query: RouterLoaderOptions;\r\n\r\n constructor( private webpack: any) { }\r\n\r\n replace(source: string): Promise<[ReplaceResult] | undefined> {\r\n //TODO: Move this regex async chaos into AST\r\n\r\n const LOAD_CHILDREN_RE = /loadChildren[\\s]*:[\\s]*['|\"](.*?)['|\"]/gm;\r\n const promises = [];\r\n let match = LOAD_CHILDREN_RE.exec(source);\r\n\r\n while (match) {\r\n const p = this.replaceSource(match[0], match[1])\r\n .then(result => {\r\n source = source.replace(result.match, result.replacement);\r\n return Object.assign(result, {\r\n source\r\n });\r\n });\r\n\r\n promises.push(p);\r\n match = LOAD_CHILDREN_RE.exec(source);\r\n }\r\n\r\n if (promises.length > 0) {\r\n return Promise.all(promises);\r\n } else {\r\n return Promise.resolve(undefined);\r\n }\r\n }\r\n\r\n private resolve(context: string, resourceUri: string): Promise {\r\n return new Promise( (resolve, reject) => {\r\n this.webpack.resolve(context, resourceUri, (err, fullPath) => {\r\n if (err) {\r\n reject(err);\r\n } else {\r\n resolve(fullPath);\r\n }\r\n });\r\n });\r\n }\r\n\r\n private replaceSource(match: string, loadChildrenPath: string): Promise {\r\n this.query = Object.assign({}, DEFAULT_OPTIONS, loaderUtils.parseQuery(this.webpack.query));\r\n\r\n const route = new RouteDestination(loadChildrenPath, this.webpack.resourcePath, this.query);\r\n\r\n const codeGen = Loader.LOADER_CODEGEN_MAP.get(route.options.loader);\r\n if (!codeGen) {\r\n return Promise.reject(new Error(`ng-router-loader - Invalid code generator \"${route.options.loader}\"`));\r\n }\r\n\r\n let context: string = !route.isRawRelative || !route.isCompiled\r\n ? path.dirname(this.webpack.resourcePath)\r\n : path.dirname(this.genDirToSourceTree(this.webpack.resourcePath))\r\n ;\r\n\r\n return this.resolve(context, route.rawFilePath)\r\n .then( (filePath) => {\r\n let moduleName = route.moduleName;\r\n\r\n // update the file path for non-ngfactory files\r\n if (this.query.aot) {\r\n filePath = this.sourceTreeToGenDir(filePath);\r\n filePath = filePath.substr(0, filePath.lastIndexOf('.'));\r\n\r\n if (route.options.bySymbol) {\r\n filePath = this.trackSymbolRootDecl(filePath, route.moduleName);\r\n }\r\n\r\n filePath = filePath + this.query.moduleSuffix;\r\n moduleName = moduleName + this.query.factorySuffix;\r\n } else {\r\n filePath = filePath.substr(0, filePath.lastIndexOf('.'));\r\n }\r\n\r\n filePath = this.normalize(filePath);\r\n\r\n const replacement = codeGen(filePath, moduleName, this.query, route.options);\r\n\r\n return {\r\n debug: typeof this.query.debug !== 'boolean' ? this.webpack.debug : this.query.debug,\r\n filePath,\r\n moduleName,\r\n resourceQuery: route.options,\r\n source: undefined,\r\n match,\r\n replacement\r\n }\r\n });\r\n }\r\n\r\n private normalize(filePath: string): string {\r\n let normalizedPath = path.normalize(filePath);\r\n\r\n if (os.platform() === 'win32') {\r\n normalizedPath = normalizedPath.replace(/\\\\/g, '\\\\\\\\');\r\n }\r\n\r\n return normalizedPath;\r\n }\r\n\r\n private trackSymbolRootDecl(absPath: string, moduleName: string): string {\r\n const summarySuffix = '.ngsummary.json';\r\n\r\n if (absPath.endsWith(summarySuffix)) {\r\n const summary = require(absPath);\r\n const symbols = summary.symbols\r\n .filter( s => s.name === moduleName)\r\n .filter( s => summary.summaries.some( ss => ss.metadata.__symbol === s.__symbol ) );\r\n\r\n const m = symbols[0];\r\n let filePath = m.filePath.replace(/^(.*)\\.d\\.ts$/, '$1');\r\n return this.trackSymbolRootDecl(this.sourceTreeToGenDir(filePath), moduleName);\r\n } else if (fs.existsSync(absPath + this.query.moduleSuffix + '.ts')) {\r\n return absPath;\r\n } else {\r\n return this.trackSymbolRootDecl(absPath + summarySuffix, moduleName);\r\n }\r\n }\r\n\r\n /**\r\n * Convert a source tree file path into a it's genDir representation\r\n * this only change the path to the file, not the file iteself (i.e: suffix)\r\n * @param absFilePath\r\n * @returns {string}\r\n */\r\n private sourceTreeToGenDir(absFilePath: string): string {\r\n if (this.query.genDir && this.query.genDir !== '.') {\r\n const relativeNgModulePath = path.relative(this.query.tsconfigDir, absFilePath);\r\n return path.join(path.resolve(this.query.tsconfigDir, this.query.genDir), relativeNgModulePath);\r\n } else {\r\n return absFilePath;\r\n }\r\n }\r\n\r\n private genDirToSourceTree(absFilePath: string): string {\r\n if (this.query.genDir && this.query.genDir !== '.') {\r\n const relativeNgModulePath = path.relative(path.resolve(this.query.tsconfigDir, this.query.genDir), absFilePath);\r\n return path.join(this.query.tsconfigDir, relativeNgModulePath);\r\n } else {\r\n return absFilePath;\r\n }\r\n }\r\n\r\n static setCodeGen(name: string, codeGen: LoaderCodeGen): void {\r\n Loader.LOADER_CODEGEN_MAP.set(name, codeGen);\r\n }\r\n static LOADER_CODEGEN_MAP = new Map();\r\n}\r\n"]} \ No newline at end of file diff --git a/dist/src/RouteModule.d.ts b/dist/src/RouteModule.d.ts new file mode 100644 index 0000000..ff1cf1b --- /dev/null +++ b/dist/src/RouteModule.d.ts @@ -0,0 +1,23 @@ +import { RouteResourceOptions, RouterLoaderOptions } from './options'; +export declare class RouteDestination { + private loadChildrenPath; + private sourceResourcePath; + private query; + readonly options: RouteResourceOptions; + /** + * The absolute file path to the destination from the import. + */ + readonly filePath: string; + readonly moduleName: string; + readonly rawFilePath: string; + /** + * If true the source resource is a angular compiler compiled module (ngFactory). + */ + readonly isCompiled: boolean; + /** + * If true the original (raw) file path is a relative path. + */ + readonly isRawRelative: boolean; + constructor(loadChildrenPath: string, sourceResourcePath: string, query: RouterLoaderOptions); + private getFilename(resourcePath); +} diff --git a/dist/src/RouteModule.js b/dist/src/RouteModule.js new file mode 100644 index 0000000..33fea04 --- /dev/null +++ b/dist/src/RouteModule.js @@ -0,0 +1,34 @@ +"use strict"; +var loaderUtils = require('loader-utils'); +var path = require('path'); +var options_1 = require('./options'); +var RouteDestination = (function () { + function RouteDestination(loadChildrenPath, sourceResourcePath, query) { + this.loadChildrenPath = loadChildrenPath; + this.sourceResourcePath = sourceResourcePath; + this.query = query; + var _a = loadChildrenPath.split('?'), pathString = _a[0], pathQuery = _a[1]; + var _b = Object.assign({}, options_1.DEFAULT_RESOURCE_OPTIONS, loaderUtils.parseQuery(pathQuery ? '?' + pathQuery : '')), loader = _b.loader, chunkName = _b.chunkName, bySymbol = _b.bySymbol; + this.options = { + loader: typeof loader === 'string' ? loader : query.loader, + chunkName: chunkName, + bySymbol: typeof bySymbol === 'boolean' ? bySymbol : query.bySymbol + }; + // split the string on the delimiter + var _c = pathString.split(query.delimiter), filePath = _c[0], moduleName = _c[1]; + this.rawFilePath = filePath; + this.moduleName = moduleName || 'default'; + this.isCompiled = this.getFilename(sourceResourcePath).endsWith(query.moduleSuffix); + this.isRawRelative = filePath.startsWith('.'); + var currentDir = path.dirname(sourceResourcePath); + // the absolute path of our destenation NgModule module. + this.filePath = path.resolve(currentDir, filePath); + } + RouteDestination.prototype.getFilename = function (resourcePath) { + var filename = path.basename(resourcePath); + return path.basename(resourcePath, path.extname(filename)); + }; + return RouteDestination; +}()); +exports.RouteDestination = RouteDestination; +//# sourceMappingURL=RouteModule.js.map \ No newline at end of file diff --git a/dist/src/RouteModule.js.map b/dist/src/RouteModule.js.map new file mode 100644 index 0000000..4335ec2 --- /dev/null +++ b/dist/src/RouteModule.js.map @@ -0,0 +1 @@ +{"version":3,"file":"RouteModule.js","sourceRoot":"","sources":["../../src/RouteModule.ts"],"names":[],"mappings":";AAAA,IAAM,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;AAC5C,IAAY,IAAI,WAAM,MAAM,CAAC,CAAA;AAC7B,wBAAoF,WAAW,CAAC,CAAA;AAEhG;IAoBE,0BAAoB,gBAAwB,EAAU,kBAA0B,EAAU,KAA0B;QAAhG,qBAAgB,GAAhB,gBAAgB,CAAQ;QAAU,uBAAkB,GAAlB,kBAAkB,CAAQ;QAAU,UAAK,GAAL,KAAK,CAAqB;QAClH,IAAA,gCAAiF,EAAzE,kBAAU,EAAE,iBAAS,CAAqD;QAClF,IAAA,oHAIC,EAJO,kBAAM,EAAE,wBAAS,EAAE,sBAAQ,CAIjC;QAEF,IAAI,CAAC,OAAO,GAAG;YACb,MAAM,EAAE,OAAO,MAAM,KAAK,QAAQ,GAAG,MAAM,GAAG,KAAK,CAAC,MAAM;YAC1D,oBAAS;YACT,QAAQ,EAAE,OAAO,QAAQ,KAAK,SAAS,GAAG,QAAQ,GAAG,KAAK,CAAC,QAAQ;SACpE,CAAC;QAEF,oCAAoC;QACpC,IAAA,sCAAsF,EAA9E,gBAAQ,EAAE,kBAAU,CAA2D;QAEvF,IAAI,CAAC,WAAW,GAAG,QAAQ,CAAC;QAC5B,IAAI,CAAC,UAAU,GAAG,UAAU,IAAI,SAAS,CAAC;QAE1C,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC,kBAAkB,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACpF,IAAI,CAAC,aAAa,GAAG,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAE9C,IAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;QACpD,wDAAwD;QACxD,IAAI,CAAC,QAAQ,GAAI,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAEtD,CAAC;IAEO,sCAAW,GAAnB,UAAoB,YAAY;QAC9B,IAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;QAE7C,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC7D,CAAC;IACH,uBAAC;AAAD,CAAC,AAtDD,IAsDC;AAtDY,wBAAgB,mBAsD5B,CAAA","sourcesContent":["const loaderUtils = require('loader-utils');\r\nimport * as path from 'path';\r\nimport { RouteResourceOptions, RouterLoaderOptions, DEFAULT_RESOURCE_OPTIONS } from './options';\r\n\r\nexport class RouteDestination {\r\n readonly options: RouteResourceOptions;\r\n\r\n /**\r\n * The absolute file path to the destination from the import.\r\n */\r\n readonly filePath: string;\r\n readonly moduleName: string;\r\n readonly rawFilePath: string;\r\n\r\n /**\r\n * If true the source resource is a angular compiler compiled module (ngFactory).\r\n */\r\n readonly isCompiled: boolean;\r\n\r\n /**\r\n * If true the original (raw) file path is a relative path.\r\n */\r\n readonly isRawRelative: boolean;\r\n\r\n constructor(private loadChildrenPath: string, private sourceResourcePath: string, private query: RouterLoaderOptions) {\r\n const [ pathString, pathQuery ] = loadChildrenPath.split('?') as [string, string];\r\n const { loader, chunkName, bySymbol } = Object.assign(\r\n {},\r\n DEFAULT_RESOURCE_OPTIONS,\r\n loaderUtils.parseQuery(pathQuery ? '?' + pathQuery : '')\r\n );\r\n\r\n this.options = {\r\n loader: typeof loader === 'string' ? loader : query.loader,\r\n chunkName,\r\n bySymbol: typeof bySymbol === 'boolean' ? bySymbol : query.bySymbol\r\n };\r\n\r\n // split the string on the delimiter\r\n const [ filePath, moduleName ] = pathString.split(query.delimiter) as [string, string];\r\n\r\n this.rawFilePath = filePath;\r\n this.moduleName = moduleName || 'default';\r\n\r\n this.isCompiled = this.getFilename(sourceResourcePath).endsWith(query.moduleSuffix);\r\n this.isRawRelative = filePath.startsWith('.');\r\n\r\n const currentDir = path.dirname(sourceResourcePath);\r\n // the absolute path of our destenation NgModule module.\r\n this.filePath = path.resolve(currentDir, filePath);\r\n\r\n }\r\n\r\n private getFilename(resourcePath): string {\r\n const filename = path.basename(resourcePath);\r\n\r\n return path.basename(resourcePath, path.extname(filename));\r\n }\r\n}\r\n"]} \ No newline at end of file diff --git a/dist/src/builtin_codegens.d.ts b/dist/src/builtin_codegens.d.ts new file mode 100644 index 0000000..edebcb1 --- /dev/null +++ b/dist/src/builtin_codegens.d.ts @@ -0,0 +1,9 @@ +import { LoaderCodeGen } from './Loader'; +export declare const syncCodeGen: LoaderCodeGen; +export declare const ensureCodeGen: LoaderCodeGen; +export declare const systemCodeGen: LoaderCodeGen; +export declare const importCodeGen: LoaderCodeGen; +export declare const BUILT_IN_CODEGENS: Array<{ + name: string; + codeGen: LoaderCodeGen; +}>; diff --git a/dist/src/builtin_codegens.js b/dist/src/builtin_codegens.js new file mode 100644 index 0000000..ee0aea6 --- /dev/null +++ b/dist/src/builtin_codegens.js @@ -0,0 +1,43 @@ +"use strict"; +function getRequireString(file, module) { + return 'require(\'' + file + '\')[\'' + module + '\']'; +} +exports.syncCodeGen = function (file, module) { return ("loadChildren: function() { return " + getRequireString(file, module) + "; }"); }; +exports.ensureCodeGen = function (file, module, loaderOptions, resourceOptions) { + var requireString = getRequireString(file, module); + var webpackChunkName = resourceOptions.chunkName ? ", '" + resourceOptions.chunkName + "'" : ''; + var result = [ + "loadChildren: function() { return new Promise(function (resolve) {", + " require.ensure([], function (require) {", + (" resolve(" + requireString + ");"), + (" }" + webpackChunkName + ");"), + "})}" + ]; + return loaderOptions.inline ? result.join('') : result.join('\n'); +}; +exports.systemCodeGen = function (file, module, opts) { + exports.systemCodeGen['deprecated'](); + var result = [ + ("loadChildren: function() { return System.import('" + file + "')"), + (".then( function(module) { return module['" + module + "']; } ); }") + ]; + return opts.inline ? result.join('') : result.join('\n'); +}; +exports.systemCodeGen['deprecated'] = function () { + console.warn('\nDEPRECATED: ng-router-loader "async-system" loader use the System.import construct which is deprecated in webpack 2 and will be removed in webpack 3, please use the "async-import" instead. (https://github.com/webpack/webpack/releases/tag/v2.1.0-beta.28)\n'); + exports.systemCodeGen['deprecated'] = function () { }; +}; +exports.importCodeGen = function (file, module, opts) { + var result = [ + ("loadChildren: function() { return import('" + file + "')"), + (" .then( function(module) { return module['" + module + "']; } ); }") + ]; + return opts.inline ? result.join('') : result.join('\n'); +}; +exports.BUILT_IN_CODEGENS = [ + { name: 'sync', codeGen: exports.syncCodeGen }, + { name: 'async-require', codeGen: exports.ensureCodeGen }, + { name: 'async-import', codeGen: exports.importCodeGen }, + { name: 'async-system', codeGen: exports.systemCodeGen } +]; +//# sourceMappingURL=builtin_codegens.js.map \ No newline at end of file diff --git a/dist/src/builtin_codegens.js.map b/dist/src/builtin_codegens.js.map new file mode 100644 index 0000000..1a72a5e --- /dev/null +++ b/dist/src/builtin_codegens.js.map @@ -0,0 +1 @@ +{"version":3,"file":"builtin_codegens.js","sourceRoot":"","sources":["../../src/builtin_codegens.ts"],"names":[],"mappings":";AAGA,0BAA0B,IAAY,EAAE,MAAc;IACpD,MAAM,CAAC,YAAY,GAAG,IAAI,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,CAAC;AACzD,CAAC;AAEY,mBAAW,GACtB,UAAC,IAAY,EAAE,MAAc,IAAK,OAAA,wCAAqC,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,SAAK,EAAxE,CAAwE,CAAC;AAEhG,qBAAa,GAAkB,UAAC,IAAY,EAAE,MAAc,EAC5B,aAAkC,EAClC,eAAqC;IAChF,IAAM,aAAa,GAAG,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACrD,IAAM,gBAAgB,GAAG,eAAe,CAAC,SAAS,GAAG,QAAM,eAAe,CAAC,SAAS,MAAG,GAAG,EAAE,CAAC;IAE7F,IAAM,MAAM,GAAG;QACb,oEAAoE;QACpE,2CAA2C;QAC3C,kBAAe,aAAa,QAAI;QAChC,SAAM,gBAAgB,QAAI;QAC1B,KAAK;KACN,CAAC;IAEF,MAAM,CAAC,aAAa,CAAC,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpE,CAAC,CAAC;AAEW,qBAAa,GAAkB,UAAC,IAAY,EAAE,MAAc,EAAE,IAAyB;IAClG,qBAAa,CAAC,YAAY,CAAC,EAAE,CAAC;IAC9B,IAAM,MAAM,GAAG;QACb,uDAAoD,IAAI,QAAI;QAC5D,+CAA4C,MAAM,gBAAY;KAC/D,CAAC;IAEF,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC3D,CAAC,CAAC;AACF,qBAAa,CAAC,YAAY,CAAC,GAAG;IAC5B,OAAO,CAAC,IAAI,CAAC,mQAAmQ,CAAC,CAAC;IAClR,qBAAa,CAAC,YAAY,CAAC,GAAG,cAAO,CAAC,CAAC;AACzC,CAAC,CAAC;AAEW,qBAAa,GAAkB,UAAC,IAAY,EAAE,MAAc,EAAE,IAAyB;IAClG,IAAM,MAAM,GAAG;QACb,gDAA6C,IAAI,QAAI;QACrD,iDAA8C,MAAM,gBAAY;KACjE,CAAC;IAEF,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC3D,CAAC,CAAC;AAGW,yBAAiB,GAAoD;IAChF,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAW,EAAE;IACtC,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,qBAAa,EAAE;IACjD,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,qBAAa,EAAE;IAChD,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,qBAAa,EAAE;CACjD,CAAC","sourcesContent":["import { LoaderCodeGen } from './Loader';\r\nimport { RouterLoaderOptions, RouteResourceOptions } from './options';\r\n\r\nfunction getRequireString(file: string, module: string): string {\r\n return 'require(\\'' + file + '\\')[\\'' + module + '\\']';\r\n}\r\n\r\nexport const syncCodeGen: LoaderCodeGen =\r\n (file: string, module: string) => `loadChildren: function() { return ${getRequireString(file, module)}; }`;\r\n\r\nexport const ensureCodeGen: LoaderCodeGen = (file: string, module: string,\r\n loaderOptions: RouterLoaderOptions,\r\n resourceOptions: RouteResourceOptions) => {\r\n const requireString = getRequireString(file, module);\r\n const webpackChunkName = resourceOptions.chunkName ? `, '${resourceOptions.chunkName}'` : '';\r\n\r\n const result = [\r\n `loadChildren: function() { return new Promise(function (resolve) {`,\r\n ` require.ensure([], function (require) {`,\r\n ` resolve(${requireString});`,\r\n ` }${webpackChunkName});`,\r\n `})}`\r\n ];\r\n\r\n return loaderOptions.inline ? result.join('') : result.join('\\n');\r\n};\r\n\r\nexport const systemCodeGen: LoaderCodeGen = (file: string, module: string, opts: RouterLoaderOptions) => {\r\n systemCodeGen['deprecated']();\r\n const result = [\r\n `loadChildren: function() { return System.import('${file}')`,\r\n `.then( function(module) { return module['${module}']; } ); }`\r\n ];\r\n\r\n return opts.inline ? result.join('') : result.join('\\n');\r\n};\r\nsystemCodeGen['deprecated'] = () => {\r\n console.warn('\\nDEPRECATED: ng-router-loader \"async-system\" loader use the System.import construct which is deprecated in webpack 2 and will be removed in webpack 3, please use the \"async-import\" instead. (https://github.com/webpack/webpack/releases/tag/v2.1.0-beta.28)\\n');\r\n systemCodeGen['deprecated'] = () => {};\r\n};\r\n\r\nexport const importCodeGen: LoaderCodeGen = (file: string, module: string, opts: RouterLoaderOptions) => {\r\n const result = [\r\n `loadChildren: function() { return import('${file}')`,\r\n ` .then( function(module) { return module['${module}']; } ); }`\r\n ];\r\n\r\n return opts.inline ? result.join('') : result.join('\\n');\r\n};\r\n\r\n\r\nexport const BUILT_IN_CODEGENS: Array<{ name: string, codeGen: LoaderCodeGen }> = [\r\n { name: 'sync', codeGen: syncCodeGen },\r\n { name: 'async-require', codeGen: ensureCodeGen },\r\n { name: 'async-import', codeGen: importCodeGen },\r\n { name: 'async-system', codeGen: systemCodeGen }\r\n];\r\n"]} \ No newline at end of file diff --git a/dist/src/options.d.ts b/dist/src/options.d.ts new file mode 100644 index 0000000..19e4a69 --- /dev/null +++ b/dist/src/options.d.ts @@ -0,0 +1,181 @@ +export declare type BUILT_IN_LOADERS = 'sync' | 'async-require' | 'async-system'; +/** + * Loader options for the `ng-router-loader`. + * + * ## Resource specific options + * Some options apply to a resource, the global option serve as the default but can be overridden in a resource URI using query parameters. + * + * >Resource specific options are marked with the **resource_override** tag. + * + * #### AOT mode + * Some of the options apply on when the `aot` flag is set to **true**. + * + * >AOT specific options are marked with the **aot_mode** tag. + * + * + * ## Usage + * #### Webpack 1 + * ```ts + * { + * test: /\.ts$/, + * loaders: [ + * 'awesome-typescript-loader', + * 'ng-router-loader?aot=true&genDir=codegen' + * ] + * } + * ``` + * + * #### Webpack 2 + * You can use the query string as well as: + * ```ts + * { + * test: /\.ts$/, + * use: [ + * 'awesome-typescript-loader', + * { + * loader: 'ng-router-loader', + * options: { + * aot: true, + * genDir: 'codegen' + * } + * } + * ] + * } + * ``` + */ +export interface RouterLoaderOptions { + /** + * A separator used to identify the NgModule class name in the URI. + * @default '#' + */ + delimiter?: string; + /** + * Enable support for AOT compiled code. + * If you are bundling AOT compiled code (.ngfactory) set this to true + * @default false + */ + aot?: boolean; + /** + * The suffix used by the angular compiler to mark compiled version of source tree modules. + * + * @aot_mode + * @default '.ngfactory' + */ + moduleSuffix?: string; + /** + * The suffix used by the angular compiler to mark compiled version of source tree NgModule class. + * + * @aot_mode + * @default 'NgFactory' + */ + factorySuffix?: string; + /** + * The code generator to use when replacing the URI with a callback. + * + * There are 3 built in code generators: + * - sync: The module will be part of the bundle (lazy initializing but NOT lazy loading) + * - async-require: The module will load in a separate bundle, using webpack's `require.ensure` feature (support chunkName) + * - async-system: The module will load in a separate bundle using System.import + * + * Note: System.import is deprecated in Webpack 2, will be removed in webpack 3 (https://github.com/webpack/webpack/releases/tag/v2.1.0-beta.28) + * + * You can override the loader in a specific resource by setting the loader in a resource query. + * + * @resource_override + * @default 'async-require' + */ + loader?: BUILT_IN_LOADERS | string; + /** + * The destination of compiled files created by the angular compiler-cli. + * The directory is resolved relative to the current working directory (process.cwd()) + * + * When genDir is empty or "." the destination is the project root and factory files are saved along side the source tree. + * + * IMPORTANT NOTE: + * The compiler-cli takes this value from the tsconfig file @ "angularCompilerOptions.genDir" and + * resolve the destination with "genDir" relative to the "tsconfig" file. + * Webpack (hence the loader) run on a different process so it does not know about that "tsconfig". + * If your setup is complex you need to make sure that the 2 "genDir"s resolve to the same directory according to the logic described here. + * + * The best scenario is when "tsconfig.json" is in the project root and webpack is executed from the project root. + * In this case the "genDir" should be identical to the "genDir" value in "tsconfig.json" + * + * @aot_mode + * @default '' + */ + genDir?: string; + /** + * If false outputs a pretty code + * + * @default true + */ + inline?: boolean; + debug?: boolean; + /** + * Resolve ngfactory modules and verify they exist, if not track the symbol and search for the ngfactory + * that exports the symbol. + * + * AOT compilation emit ngfactory files only for NgModule, Component and Directive. + * If a module does not have one of those the compiler will not create an ngfactory file for it. + * + * In webpack it is a common practice to use directories as packages (barrel pattern) having an + * index file exporting what's needed. + * + * When a URI reference a module (ModA) that: + * - export an NgModule defined in an other module (ModB) and; + * - ModA has no NgModule, Component or Directive defined inside it + * + * The resolved file will "ModA.ngfactory", even though it does not exists. + * When bySymbol is true the loader will use the metadata in "ngsummary.json" files to lookup the + * first ngfactory file that exports ModB, it will then use it as the resolved URI. + * + * You can override this option in a specific resource by setting the bySymbol property in a resource query. + * + * @resource_override + * @aot_mode + * @default true + */ + bySymbol?: boolean; + /** + * For complex folder structures it could be necessary to set de full path of the tsconfig file + * If your setup is complex sometimes resolving the 2 "genDir"s to the same directory is not enough + * If you don't set this option, the tsconfig will be relative to the process.cwd() directory + * @aot_mode + * @default 'process.cwd()' + */ + tsconfigDir?: string; +} +/** + * Resource specific options, defined as query string on the resource URI. + * + * ``` + * { + * path: 'home', + * loadChildren: './app/home/home-module#HomeModule?loader=sync&bySymbol=false' + * } + * ``` + */ +export interface RouteResourceOptions { + /** + * The code generator to use when replacing the URI with a callback. + * + * @resource_override RouterLoaderOptions.loader + */ + loader?: BUILT_IN_LOADERS | string; + /** + * A chunk name used by webpack to bundle async modules. + * Used by the "async-require" loader. + * + * For more information see [webpack require.ensure](https://webpack.js.org/guides/code-splitting/#code-splitting-with-require-ensure-) + */ + chunkName?: string; + /** + * Resolve ngfactory modules and verify they exist, if not track the symbol and search for the ngfactory + * that exports the symbol. + * + * @resource_override RouterLoaderOptions.loader + */ + bySymbol?: boolean; +} +export declare const DEFAULT_OPTIONS: RouterLoaderOptions; +export declare const DEFAULT_RESOURCE_OPTIONS: RouteResourceOptions; diff --git a/dist/src/options.js b/dist/src/options.js new file mode 100644 index 0000000..57a1b17 --- /dev/null +++ b/dist/src/options.js @@ -0,0 +1,18 @@ +"use strict"; +exports.DEFAULT_OPTIONS = { + delimiter: '#', + aot: false, + moduleSuffix: '.ngfactory', + factorySuffix: 'NgFactory', + loader: 'async-require', + genDir: '', + inline: true, + bySymbol: true, + tsconfigDir: process.cwd() +}; +exports.DEFAULT_RESOURCE_OPTIONS = { + loader: undefined, + chunkName: undefined, + bySymbol: undefined +}; +//# sourceMappingURL=options.js.map \ No newline at end of file diff --git a/dist/src/options.js.map b/dist/src/options.js.map new file mode 100644 index 0000000..77e547a --- /dev/null +++ b/dist/src/options.js.map @@ -0,0 +1 @@ +{"version":3,"file":"options.js","sourceRoot":"","sources":["../../src/options.ts"],"names":[],"mappings":";AAgMa,uBAAe,GAAwB;IAClD,SAAS,EAAE,GAAG;IACd,GAAG,EAAE,KAAK;IACV,YAAY,EAAE,YAAY;IAC1B,aAAa,EAAE,WAAW;IAC1B,MAAM,EAAE,eAAe;IACvB,MAAM,EAAE,EAAE;IACV,MAAM,EAAE,IAAI;IACZ,QAAQ,EAAE,IAAI;IACd,WAAW,EAAE,OAAO,CAAC,GAAG,EAAE;CAC3B,CAAC;AAEW,gCAAwB,GAAyB;IAC5D,MAAM,EAAE,SAAS;IACjB,SAAS,EAAE,SAAS;IACpB,QAAQ,EAAE,SAAS;CACpB,CAAC","sourcesContent":["export type BUILT_IN_LOADERS = 'sync' | 'async-require' | 'async-system';\r\n\r\n/**\r\n * Loader options for the `ng-router-loader`.\r\n *\r\n * ## Resource specific options\r\n * Some options apply to a resource, the global option serve as the default but can be overridden in a resource URI using query parameters.\r\n *\r\n * >Resource specific options are marked with the **resource_override** tag.\r\n *\r\n * #### AOT mode\r\n * Some of the options apply on when the `aot` flag is set to **true**.\r\n *\r\n * >AOT specific options are marked with the **aot_mode** tag.\r\n *\r\n *\r\n * ## Usage\r\n * #### Webpack 1\r\n * ```ts\r\n * {\r\n * test: /\\.ts$/,\r\n * loaders: [\r\n * 'awesome-typescript-loader',\r\n * 'ng-router-loader?aot=true&genDir=codegen'\r\n * ]\r\n * }\r\n * ```\r\n *\r\n * #### Webpack 2\r\n * You can use the query string as well as:\r\n * ```ts\r\n * {\r\n * test: /\\.ts$/,\r\n * use: [\r\n * 'awesome-typescript-loader',\r\n * {\r\n * loader: 'ng-router-loader',\r\n * options: {\r\n * aot: true,\r\n * genDir: 'codegen'\r\n * }\r\n * }\r\n * ]\r\n * }\r\n * ```\r\n */\r\nexport interface RouterLoaderOptions {\r\n /**\r\n * A separator used to identify the NgModule class name in the URI.\r\n * @default '#'\r\n */\r\n delimiter?: string;\r\n\r\n /**\r\n * Enable support for AOT compiled code.\r\n * If you are bundling AOT compiled code (.ngfactory) set this to true\r\n * @default false\r\n */\r\n aot?: boolean;\r\n\r\n /**\r\n * The suffix used by the angular compiler to mark compiled version of source tree modules.\r\n *\r\n * @aot_mode\r\n * @default '.ngfactory'\r\n */\r\n moduleSuffix?: string;\r\n\r\n /**\r\n * The suffix used by the angular compiler to mark compiled version of source tree NgModule class.\r\n *\r\n * @aot_mode\r\n * @default 'NgFactory'\r\n */\r\n factorySuffix?: string;\r\n\r\n /**\r\n * The code generator to use when replacing the URI with a callback.\r\n *\r\n * There are 3 built in code generators:\r\n * - sync: The module will be part of the bundle (lazy initializing but NOT lazy loading)\r\n * - async-require: The module will load in a separate bundle, using webpack's `require.ensure` feature (support chunkName)\r\n * - async-system: The module will load in a separate bundle using System.import\r\n *\r\n * Note: System.import is deprecated in Webpack 2, will be removed in webpack 3 (https://github.com/webpack/webpack/releases/tag/v2.1.0-beta.28)\r\n *\r\n * You can override the loader in a specific resource by setting the loader in a resource query.\r\n *\r\n * @resource_override\r\n * @default 'async-require'\r\n */\r\n loader?: BUILT_IN_LOADERS | string;\r\n\r\n /**\r\n * The destination of compiled files created by the angular compiler-cli.\r\n * The directory is resolved relative to the current working directory (process.cwd())\r\n *\r\n * When genDir is empty or \".\" the destination is the project root and factory files are saved along side the source tree.\r\n *\r\n * IMPORTANT NOTE:\r\n * The compiler-cli takes this value from the tsconfig file @ \"angularCompilerOptions.genDir\" and\r\n * resolve the destination with \"genDir\" relative to the \"tsconfig\" file.\r\n * Webpack (hence the loader) run on a different process so it does not know about that \"tsconfig\".\r\n * If your setup is complex you need to make sure that the 2 \"genDir\"s resolve to the same directory according to the logic described here.\r\n *\r\n * The best scenario is when \"tsconfig.json\" is in the project root and webpack is executed from the project root.\r\n * In this case the \"genDir\" should be identical to the \"genDir\" value in \"tsconfig.json\"\r\n *\r\n * @aot_mode\r\n * @default ''\r\n */\r\n genDir?: string;\r\n\r\n /**\r\n * If false outputs a pretty code\r\n *\r\n * @default true\r\n */\r\n inline?: boolean;\r\n\r\n debug?: boolean;\r\n\r\n /**\r\n * Resolve ngfactory modules and verify they exist, if not track the symbol and search for the ngfactory\r\n * that exports the symbol.\r\n *\r\n * AOT compilation emit ngfactory files only for NgModule, Component and Directive.\r\n * If a module does not have one of those the compiler will not create an ngfactory file for it.\r\n *\r\n * In webpack it is a common practice to use directories as packages (barrel pattern) having an\r\n * index file exporting what's needed.\r\n *\r\n * When a URI reference a module (ModA) that:\r\n * - export an NgModule defined in an other module (ModB) and;\r\n * - ModA has no NgModule, Component or Directive defined inside it\r\n *\r\n * The resolved file will \"ModA.ngfactory\", even though it does not exists.\r\n * When bySymbol is true the loader will use the metadata in \"ngsummary.json\" files to lookup the\r\n * first ngfactory file that exports ModB, it will then use it as the resolved URI.\r\n *\r\n * You can override this option in a specific resource by setting the bySymbol property in a resource query.\r\n *\r\n * @resource_override\r\n * @aot_mode\r\n * @default true\r\n */\r\n bySymbol?: boolean;\r\n /**\r\n * For complex folder structures it could be necessary to set de full path of the tsconfig file\r\n * If your setup is complex sometimes resolving the 2 \"genDir\"s to the same directory is not enough\r\n * If you don't set this option, the tsconfig will be relative to the process.cwd() directory\r\n * @aot_mode\r\n * @default 'process.cwd()'\r\n */\r\n tsconfigDir?: string;\r\n}\r\n\r\n/**\r\n * Resource specific options, defined as query string on the resource URI.\r\n *\r\n * ```\r\n * {\r\n * path: 'home',\r\n * loadChildren: './app/home/home-module#HomeModule?loader=sync&bySymbol=false'\r\n * }\r\n * ```\r\n */\r\nexport interface RouteResourceOptions {\r\n /**\r\n * The code generator to use when replacing the URI with a callback.\r\n *\r\n * @resource_override RouterLoaderOptions.loader\r\n */\r\n loader?: BUILT_IN_LOADERS | string;\r\n\r\n /**\r\n * A chunk name used by webpack to bundle async modules.\r\n * Used by the \"async-require\" loader.\r\n *\r\n * For more information see [webpack require.ensure](https://webpack.js.org/guides/code-splitting/#code-splitting-with-require-ensure-)\r\n */\r\n chunkName?: string;\r\n\r\n /**\r\n * Resolve ngfactory modules and verify they exist, if not track the symbol and search for the ngfactory\r\n * that exports the symbol.\r\n *\r\n * @resource_override RouterLoaderOptions.loader\r\n */\r\n bySymbol?: boolean;\r\n}\r\n\r\nexport const DEFAULT_OPTIONS: RouterLoaderOptions = {\r\n delimiter: '#',\r\n aot: false,\r\n moduleSuffix: '.ngfactory',\r\n factorySuffix: 'NgFactory',\r\n loader: 'async-require',\r\n genDir: '',\r\n inline: true,\r\n bySymbol: true,\r\n tsconfigDir: process.cwd()\r\n};\r\n\r\nexport const DEFAULT_RESOURCE_OPTIONS: RouteResourceOptions = {\r\n loader: undefined,\r\n chunkName: undefined,\r\n bySymbol: undefined\r\n};\r\n\r\n"]} \ No newline at end of file diff --git a/package.json b/package.json index 19f85b4..618d603 100644 --- a/package.json +++ b/package.json @@ -19,12 +19,12 @@ "lazy-loading" ], "scripts": { - "clean:ngc": "rm -rf __codegen__", - "clean:docs": "rm -rf docs/docs_dist", - "clean:dist": "rm -rf dist", + "clean:ngc": "rm -rf ./__codegen__", + "clean:docs": "rm -rf ./docs/docs_dist", + "clean:dist": "rm -rf ./dist", "ci": "npm run test", "test": "npm run compile_integration && npm run build && ./node_modules/.bin/mocha dist/test spec --recursive", - "build": "npm run clean:dist && ./node_modules/.bin/tsc", + "build": "./node_modules/.bin/tsc", "watch": "npm run build -- -w", "compile_integration": "npm run clean:ngc && ./node_modules/.bin/ngc -p tsconfig.integration.json", "docs": "npm run clean:docs && cd docs && ../node_modules/.bin/typedoc --options typedoc.json --excludePrivate" @@ -52,6 +52,7 @@ "@types/mocha": "^2.2.35", "@types/node": "^6.0.55", "@types/webpack": "^2.1.0", + "@types/lodash": "ts2.0", "chai": "^3.5.0", "mocha": "^3.2.0", "rxjs": "^5.0.2", diff --git a/src/Loader.ts b/src/Loader.ts index 4d96f4d..18ca74c 100644 --- a/src/Loader.ts +++ b/src/Loader.ts @@ -181,8 +181,8 @@ export class Loader { */ private sourceTreeToGenDir(absFilePath: string): string { if (this.query.genDir && this.query.genDir !== '.') { - const relativeNgModulePath = path.relative(process.cwd(), absFilePath); - return path.join(path.resolve(process.cwd(), this.query.genDir), relativeNgModulePath); + const relativeNgModulePath = path.relative(this.query.tsconfigDir, absFilePath); + return path.join(path.resolve(this.query.tsconfigDir, this.query.genDir), relativeNgModulePath); } else { return absFilePath; } @@ -190,8 +190,8 @@ export class Loader { private genDirToSourceTree(absFilePath: string): string { if (this.query.genDir && this.query.genDir !== '.') { - const relativeNgModulePath = path.relative(path.resolve(process.cwd(), this.query.genDir), absFilePath); - return path.join(process.cwd(), relativeNgModulePath); + const relativeNgModulePath = path.relative(path.resolve(this.query.tsconfigDir, this.query.genDir), absFilePath); + return path.join(this.query.tsconfigDir, relativeNgModulePath); } else { return absFilePath; } diff --git a/src/options.ts b/src/options.ts index 6a2f4c3..1e50367 100644 --- a/src/options.ts +++ b/src/options.ts @@ -145,6 +145,14 @@ export interface RouterLoaderOptions { * @default true */ bySymbol?: boolean; + /** + * For complex folder structures it could be necessary to set de full path of the tsconfig file + * If your setup is complex sometimes resolving the 2 "genDir"s to the same directory is not enough + * If you don't set this option, the tsconfig will be relative to the process.cwd() directory + * @aot_mode + * @default 'process.cwd()' + */ + tsconfigDir?: string; } /** @@ -190,7 +198,8 @@ export const DEFAULT_OPTIONS: RouterLoaderOptions = { loader: 'async-require', genDir: '', inline: true, - bySymbol: true + bySymbol: true, + tsconfigDir: process.cwd() }; export const DEFAULT_RESOURCE_OPTIONS: RouteResourceOptions = {