diff --git a/packages/ngtools/webpack/src/paths-plugin.ts b/packages/ngtools/webpack/src/paths-plugin.ts index 2fdbecea7d..39d73d9f63 100644 --- a/packages/ngtools/webpack/src/paths-plugin.ts +++ b/packages/ngtools/webpack/src/paths-plugin.ts @@ -160,15 +160,40 @@ export function resolveWithPaths( const jsFilePath = `${pathNoExtension}.js`; if (host.fileExists(pathNoExtension)) { - // This is mainly for secondary entry points - // ex: 'node_modules/@angular/core/testing.d.ts' -> 'node_modules/@angular/core/testing' - request.request = pathNoExtension; - } else if (host.fileExists(packageRootPath)) { - // Let webpack resolve the correct module format - request.request = pathDirName; - } else if (host.fileExists(jsFilePath)) { + // This is mainly for secondary entry points + // ex: 'node_modules/@angular/core/testing.d.ts' -> 'node_modules/@angular/core/testing' + request.request = pathNoExtension; + } else { + const packageJsonContent = host.readFile(packageRootPath); + let newRequest: string | undefined; + + if (packageJsonContent) { + try { + const packageJson = JSON.parse(packageJsonContent); + + // Let webpack resolve the correct module format IIF there is a module resolution field + // in the package.json. These are all official fields that Angular uses. + if (typeof packageJson.main == 'string' + || typeof packageJson.browser == 'string' + || typeof packageJson.module == 'string' + || typeof packageJson.es2015 == 'string' + || typeof packageJson.fesm5 == 'string' + || typeof packageJson.fesm2015 == 'string') { + newRequest = pathDirName; + } + } catch { + // Ignore exceptions and let it fall through (ie. if package.json file is invalid). + } + } + + if (newRequest === undefined && host.fileExists(jsFilePath)) { // Otherwise, if there is a file with a .js extension use that - request.request = jsFilePath; + newRequest = jsFilePath; + } + + if (newRequest !== undefined) { + request.request = newRequest; + } } callback(null, request); diff --git a/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/aotplugin.config.json b/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/aotplugin.config.json new file mode 100644 index 0000000000..1f181c2807 --- /dev/null +++ b/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/aotplugin.config.json @@ -0,0 +1,4 @@ +{ + "tsConfigPath": "./src/tsconfig.json", + "mainPath": "app/main.jit.ts" +} \ No newline at end of file diff --git a/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/package.json b/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/package.json new file mode 100644 index 0000000000..4a2637e90a --- /dev/null +++ b/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/package.json @@ -0,0 +1,29 @@ +{ + "name": "test", + "license": "MIT", + "dependencies": { + "@angular/common": "^5.0.0-rc.8", + "@angular/compiler": "^5.0.0-rc.8", + "@angular/compiler-cli": "^5.0.0-rc.8", + "@angular/core": "^5.0.0-rc.8", + "@angular/http": "^5.0.0-rc.8", + "@angular/platform-browser": "^5.0.0-rc.8", + "@angular/platform-browser-dynamic": "^5.0.0-rc.8", + "@angular/platform-server": "^5.0.0-rc.8", + "@angular/router": "^5.0.0-rc.8", + "@ngtools/webpack": "0.0.0", + "core-js": "^2.4.1", + "rxjs": "^5.5.0", + "zone.js": "^0.8.14" + }, + "devDependencies": { + "node-sass": "^4.7.0", + "performance-now": "^0.2.0", + "preprocess-loader": "^0.2.2", + "raw-loader": "^0.5.1", + "sass-loader": "^6.0.0", + "typescript": "~2.4.2", + "webpack": "~4.0.1", + "webpack-cli": "~2.0.9" + } +} diff --git a/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/src/app/app.module.ts b/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/src/app/app.module.ts new file mode 100644 index 0000000000..1820fbcaf2 --- /dev/null +++ b/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/src/app/app.module.ts @@ -0,0 +1,29 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import { Component, NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { Foo } from 'foo/foo'; + +console.log(Foo); // Make sure it's used. + +@Component({ + selector: 'home-view', + template: 'home!', +}) +export class HomeView {} + +@NgModule({ + declarations: [ + HomeView, + ], + imports: [ + BrowserModule, + ], + bootstrap: [HomeView], +}) +export class AppModule { } diff --git a/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/src/app/main.jit.ts b/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/src/app/main.jit.ts new file mode 100644 index 0000000000..0a705a5dae --- /dev/null +++ b/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/src/app/main.jit.ts @@ -0,0 +1,5 @@ +import 'core-js/es7/reflect'; +import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; +import {AppModule} from './app.module'; + +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/src/foo/foo.d.ts b/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/src/foo/foo.d.ts new file mode 100644 index 0000000000..c1e8a56b1a --- /dev/null +++ b/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/src/foo/foo.d.ts @@ -0,0 +1,9 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +export class Foo {} diff --git a/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/src/foo/foo.js b/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/src/foo/foo.js new file mode 100644 index 0000000000..77fd187ca6 --- /dev/null +++ b/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/src/foo/foo.js @@ -0,0 +1,6 @@ + +console.log('NGTOOLS_WEBPACK_TEST_WRONG_FILE'); + +module.exports = { + Foo: () => {}, // Empty "class". +}; diff --git a/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/src/foo/package.json b/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/src/foo/package.json new file mode 100644 index 0000000000..df34a12157 --- /dev/null +++ b/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/src/foo/package.json @@ -0,0 +1,3 @@ +{ + "main": "right/foo.js" +} diff --git a/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/src/foo/right/foo.js b/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/src/foo/right/foo.js new file mode 100644 index 0000000000..9947c559a8 --- /dev/null +++ b/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/src/foo/right/foo.js @@ -0,0 +1,6 @@ + +console.log('NGTOOLS_WEBPACK_TEST_RIGHT_FILE'); + +module.exports = { + Foo: () => {}, // Empty "class". +}; diff --git a/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/src/index.html b/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/src/index.html new file mode 100644 index 0000000000..89fb0893c3 --- /dev/null +++ b/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/src/index.html @@ -0,0 +1,12 @@ + + + + Document + + + + + + + + diff --git a/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/src/tsconfig.json b/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/src/tsconfig.json new file mode 100644 index 0000000000..fd2f8b66e9 --- /dev/null +++ b/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/src/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "module": "es2015", + "moduleResolution": "node", + "target": "es5", + "baseUrl": ".", + "noImplicitAny": false, + "sourceMap": true, + "mapRoot": "", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "lib": [ + "es2017", + "dom" + ], + "outDir": "lib", + "skipLibCheck": true, + "rootDir": ".", + "paths": { + "foo/*": [ + "./foo/*", + "*" + ] + } + }, + "angularCompilerOptions": { + "genDir": "app/generated/", + "entryModule": "app/app.module#AppModule" + } +} diff --git a/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/webpack.config.js b/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/webpack.config.js new file mode 100644 index 0000000000..5175b36bb7 --- /dev/null +++ b/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/webpack.config.js @@ -0,0 +1,45 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +const ngToolsWebpack = require('@ngtools/webpack'); +const path = require('path'); + +const flags = require('./webpack.flags.json'); + +const preprocessLoader = 'preprocess-loader' + (flags.DEBUG ? '?+DEBUG' : ''); + + +module.exports = { + resolve: { + extensions: ['.ts', '.js'] + }, + entry: './src/app/main.jit.ts', + output: { + path: path.resolve('./dist'), + publicPath: 'dist/', + filename: 'app.main.js' + }, + plugins: [ + new ngToolsWebpack.AngularCompilerPlugin(require('./aotplugin.config.json')) + ], + module: { + rules: [ + { test: /\.scss$/, loaders: ['raw-loader', 'sass-loader', preprocessLoader] }, + { test: /\.css$/, loader: 'raw-loader' }, + { test: /\.html$/, loaders: ['raw-loader', preprocessLoader] }, + // Use preprocess to remove DEBUG only code. + // @ngtools/webpack must be the first (right most) loader. + { test: /\.ts$/, use: [ + { loader: preprocessLoader }, + { loader: '@ngtools/webpack' } + ] } + ] + }, + devServer: { + historyApiFallback: true + } +}; diff --git a/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/webpack.flags.json b/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/webpack.flags.json new file mode 100644 index 0000000000..aac05f738d --- /dev/null +++ b/tests/legacy-cli/e2e/assets/webpack/test-app-path-mapping/webpack.flags.json @@ -0,0 +1,3 @@ +{ + "DEBUG": false +} diff --git a/tests/legacy-cli/e2e/tests/packages/webpack/test-path-mapping.ts b/tests/legacy-cli/e2e/tests/packages/webpack/test-path-mapping.ts new file mode 100644 index 0000000000..9e72e6d0d2 --- /dev/null +++ b/tests/legacy-cli/e2e/tests/packages/webpack/test-path-mapping.ts @@ -0,0 +1,13 @@ +import {normalize} from 'path'; +import {createProjectFromAsset} from '../../../utils/assets'; +import {exec} from '../../../utils/process'; +import {expectFileSizeToBeUnder, replaceInFile, expectFileToMatch} from '../../../utils/fs'; + + +export default function(skipCleaning: () => void) { + return Promise.resolve() + .then(() => createProjectFromAsset('webpack/test-app-path-mapping')) + .then(() => exec(normalize('node_modules/.bin/webpack-cli'))) + .then(() => expectFileToMatch('dist/app.main.js', 'NGTOOLS_WEBPACK_TEST_RIGHT_FILE')) + .then(() => skipCleaning()); +}