Skip to content

Commit 9c5662b

Browse files
committed
fix(@ngtools/webpack): fix resolution fallback of paths-plugin
If there is a package.json we should also verify that it has a main or fesm field to see if webpack would actually resolve it properly. Otherwise use the JavaScript resolution. This is a temporary fix and still has obvious limitations and issues. Namely, this code is never run if there is only one path mapping, but that falls outside the scope of this fix. Also, some people might have valid packages but want to resolve to the JS file itself (which is what TypeScript does by default). These should be fixed with a refactoring of the path plugin. Fixes NativeScript/nativescript-dev-webpack#607
1 parent 950cd3e commit 9c5662b

File tree

14 files changed

+222
-8
lines changed

14 files changed

+222
-8
lines changed

Diff for: packages/ngtools/webpack/src/paths-plugin.ts

+28-8
Original file line numberDiff line numberDiff line change
@@ -160,15 +160,35 @@ export function resolveWithPaths(
160160
const jsFilePath = `${pathNoExtension}.js`;
161161

162162
if (host.fileExists(pathNoExtension)) {
163-
// This is mainly for secondary entry points
164-
// ex: 'node_modules/@angular/core/testing.d.ts' -> 'node_modules/@angular/core/testing'
165-
request.request = pathNoExtension;
166-
} else if (host.fileExists(packageRootPath)) {
167-
// Let webpack resolve the correct module format
168-
request.request = pathDirName;
169-
} else if (host.fileExists(jsFilePath)) {
163+
// This is mainly for secondary entry points
164+
// ex: 'node_modules/@angular/core/testing.d.ts' -> 'node_modules/@angular/core/testing'
165+
request.request = pathNoExtension;
166+
} else {
167+
const packageJsonContent = host.readFile(packageRootPath);
168+
let newRequest: string | undefined;
169+
170+
if (packageJsonContent) {
171+
try {
172+
const packageJson = JSON.parse(packageJsonContent);
173+
174+
// Let webpack resolve the correct module format IIF there is a main field in the
175+
// package.json.
176+
if (packageJson.main) {
177+
newRequest = pathDirName;
178+
}
179+
} catch {
180+
// Ignore exceptions and let it fall through (ie. if package.json file is invalid).
181+
}
182+
}
183+
184+
if (newRequest === undefined && host.fileExists(jsFilePath)) {
170185
// Otherwise, if there is a file with a .js extension use that
171-
request.request = jsFilePath;
186+
newRequest = jsFilePath;
187+
}
188+
189+
if (newRequest !== undefined) {
190+
request.request = newRequest;
191+
}
172192
}
173193

174194
callback(null, request);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"tsConfigPath": "./src/tsconfig.json",
3+
"mainPath": "app/main.jit.ts"
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"name": "test",
3+
"license": "MIT",
4+
"dependencies": {
5+
"@angular/common": "^5.0.0-rc.8",
6+
"@angular/compiler": "^5.0.0-rc.8",
7+
"@angular/compiler-cli": "^5.0.0-rc.8",
8+
"@angular/core": "^5.0.0-rc.8",
9+
"@angular/http": "^5.0.0-rc.8",
10+
"@angular/platform-browser": "^5.0.0-rc.8",
11+
"@angular/platform-browser-dynamic": "^5.0.0-rc.8",
12+
"@angular/platform-server": "^5.0.0-rc.8",
13+
"@angular/router": "^5.0.0-rc.8",
14+
"@ngtools/webpack": "0.0.0",
15+
"core-js": "^2.4.1",
16+
"rxjs": "^5.5.0",
17+
"zone.js": "^0.8.14"
18+
},
19+
"devDependencies": {
20+
"node-sass": "^4.7.0",
21+
"performance-now": "^0.2.0",
22+
"preprocess-loader": "^0.2.2",
23+
"raw-loader": "^0.5.1",
24+
"sass-loader": "^6.0.0",
25+
"typescript": "~2.4.2",
26+
"webpack": "~4.0.1",
27+
"webpack-cli": "~2.0.9"
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
import { Component, NgModule } from '@angular/core';
9+
import { BrowserModule } from '@angular/platform-browser';
10+
import { Foo } from 'foo/foo';
11+
12+
console.log(Foo); // Make sure it's used.
13+
14+
@Component({
15+
selector: 'home-view',
16+
template: 'home!',
17+
})
18+
export class HomeView {}
19+
20+
@NgModule({
21+
declarations: [
22+
HomeView,
23+
],
24+
imports: [
25+
BrowserModule,
26+
],
27+
bootstrap: [HomeView],
28+
})
29+
export class AppModule { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import 'core-js/es7/reflect';
2+
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
3+
import {AppModule} from './app.module';
4+
5+
platformBrowserDynamic().bootstrapModule(AppModule);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
export class Foo {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
console.log('NGTOOLS_WEBPACK_TEST_WRONG_FILE');
3+
4+
module.exports = {
5+
Foo: () => {}, // Empty "class".
6+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"main": "right/foo.js"
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
console.log('NGTOOLS_WEBPACK_TEST_RIGHT_FILE');
3+
4+
module.exports = {
5+
Foo: () => {}, // Empty "class".
6+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<html>
2+
<head>
3+
<meta charset="UTF-8">
4+
<title>Document</title>
5+
<base href="">
6+
</head>
7+
<body>
8+
<app-root></app-root>
9+
<script src="node_modules/zone.js/dist/zone.js"></script>
10+
<script src="dist/app.main.js"></script>
11+
</body>
12+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"compilerOptions": {
3+
"module": "es2015",
4+
"moduleResolution": "node",
5+
"target": "es5",
6+
"baseUrl": ".",
7+
"noImplicitAny": false,
8+
"sourceMap": true,
9+
"mapRoot": "",
10+
"emitDecoratorMetadata": true,
11+
"experimentalDecorators": true,
12+
"lib": [
13+
"es2017",
14+
"dom"
15+
],
16+
"outDir": "lib",
17+
"skipLibCheck": true,
18+
"rootDir": ".",
19+
"paths": {
20+
"foo/*": [
21+
"./foo/*",
22+
"*"
23+
]
24+
}
25+
},
26+
"angularCompilerOptions": {
27+
"genDir": "app/generated/",
28+
"entryModule": "app/app.module#AppModule"
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
const ngToolsWebpack = require('@ngtools/webpack');
9+
const path = require('path');
10+
11+
const flags = require('./webpack.flags.json');
12+
13+
const preprocessLoader = 'preprocess-loader' + (flags.DEBUG ? '?+DEBUG' : '');
14+
15+
16+
module.exports = {
17+
resolve: {
18+
extensions: ['.ts', '.js']
19+
},
20+
entry: './src/app/main.jit.ts',
21+
output: {
22+
path: path.resolve('./dist'),
23+
publicPath: 'dist/',
24+
filename: 'app.main.js'
25+
},
26+
plugins: [
27+
new ngToolsWebpack.AngularCompilerPlugin(require('./aotplugin.config.json'))
28+
],
29+
module: {
30+
rules: [
31+
{ test: /\.scss$/, loaders: ['raw-loader', 'sass-loader', preprocessLoader] },
32+
{ test: /\.css$/, loader: 'raw-loader' },
33+
{ test: /\.html$/, loaders: ['raw-loader', preprocessLoader] },
34+
// Use preprocess to remove DEBUG only code.
35+
// @ngtools/webpack must be the first (right most) loader.
36+
{ test: /\.ts$/, use: [
37+
{ loader: preprocessLoader },
38+
{ loader: '@ngtools/webpack' }
39+
] }
40+
]
41+
},
42+
devServer: {
43+
historyApiFallback: true
44+
}
45+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"DEBUG": false
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import {normalize} from 'path';
2+
import {createProjectFromAsset} from '../../../utils/assets';
3+
import {exec} from '../../../utils/process';
4+
import {expectFileSizeToBeUnder, replaceInFile, expectFileToMatch} from '../../../utils/fs';
5+
6+
7+
export default function(skipCleaning: () => void) {
8+
return Promise.resolve()
9+
.then(() => createProjectFromAsset('webpack/test-app-path-mapping'))
10+
.then(() => exec(normalize('node_modules/.bin/webpack-cli')))
11+
.then(() => expectFileToMatch('dist/app.main.js', 'NGTOOLS_WEBPACK_TEST_RIGHT_FILE'))
12+
.then(() => skipCleaning());
13+
}

0 commit comments

Comments
 (0)