Skip to content

Commit b2ade63

Browse files
hanslBrocco
authored andcommitted
fix(@ngtools/webpack): support webpack loaders in the webpack plugin
This is only useful when using webpack directly. Does not affect the CLI. Performance is going to be impacted, but we do not type check after the loaders. TODO: add typechecking if the source we receive from Webpack is different from the host.
1 parent fa8e97f commit b2ade63

File tree

9 files changed

+95
-12
lines changed

9 files changed

+95
-12
lines changed

packages/@ngtools/webpack/src/loader.ts

+26-2
Original file line numberDiff line numberDiff line change
@@ -436,15 +436,20 @@ function _diagnoseDeps(reasons: ModuleReason[], plugin: AotPlugin, checked: Set<
436436

437437

438438
// Super simple TS transpiler loader for testing / isolated usage. does not type check!
439-
export function ngcLoader(this: LoaderContext & { _compilation: any }) {
439+
export function ngcLoader(this: LoaderContext & { _compilation: any }, source: string | null) {
440440
const cb = this.async();
441441
const sourceFileName: string = this.resourcePath;
442442

443443
const plugin = this._compilation._ngToolsWebpackPluginInstance as AotPlugin;
444444
// We must verify that AotPlugin is an instance of the right class.
445445
if (plugin && plugin instanceof AotPlugin) {
446+
if (plugin.compilerHost.readFile(sourceFileName) == source) {
447+
// In the case where the source is the same as the one in compilerHost, we don't have
448+
// extra TS loaders and there's no need to do any trickery.
449+
source = null;
450+
}
446451
const refactor = new TypeScriptFileRefactor(
447-
sourceFileName, plugin.compilerHost, plugin.program);
452+
sourceFileName, plugin.compilerHost, plugin.program, source);
448453

449454
Promise.resolve()
450455
.then(() => {
@@ -476,6 +481,25 @@ export function ngcLoader(this: LoaderContext & { _compilation: any }) {
476481
});
477482
})
478483
.then(() => {
484+
if (source) {
485+
// We need to validate diagnostics. We ignore type checking though, to save time.
486+
const diagnostics = refactor.getDiagnostics(false);
487+
if (diagnostics.length) {
488+
let message = '';
489+
490+
diagnostics.forEach(diagnostic => {
491+
const position = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
492+
493+
const fileName = diagnostic.file.fileName;
494+
const {line, character} = position;
495+
496+
const messageText = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
497+
message += `${fileName} (${line + 1},${character + 1}): ${messageText}\n`;
498+
});
499+
throw new Error(message);
500+
}
501+
}
502+
479503
// Force a few compiler options to make sure we get the result we want.
480504
const compilerOptions: ts.CompilerOptions = Object.assign({}, plugin.compilerOptions, {
481505
inlineSources: true,

packages/@ngtools/webpack/src/refactor.ts

+10-5
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,19 @@ export class TypeScriptFileRefactor {
3838

3939
constructor(fileName: string,
4040
_host: ts.CompilerHost,
41-
private _program?: ts.Program) {
41+
private _program?: ts.Program,
42+
source?: string | null) {
4243
fileName = resolve(fileName, _host, _program).replace(/\\/g, '/');
4344
this._fileName = fileName;
4445
if (_program) {
45-
this._sourceFile = _program.getSourceFile(fileName);
46+
if (source) {
47+
this._sourceFile = ts.createSourceFile(fileName, source, ts.ScriptTarget.Latest, true);
48+
} else {
49+
this._sourceFile = _program.getSourceFile(fileName);
50+
}
4651
}
4752
if (!this._sourceFile) {
48-
this._sourceFile = ts.createSourceFile(fileName, _host.readFile(fileName),
53+
this._sourceFile = ts.createSourceFile(fileName, source || _host.readFile(fileName),
4954
ts.ScriptTarget.Latest, true);
5055
}
5156
this._sourceText = this._sourceFile.getFullText(this._sourceFile);
@@ -55,7 +60,7 @@ export class TypeScriptFileRefactor {
5560
/**
5661
* Collates the diagnostic messages for the current source file
5762
*/
58-
getDiagnostics(): ts.Diagnostic[] {
63+
getDiagnostics(typeCheck = true): ts.Diagnostic[] {
5964
if (!this._program) {
6065
return [];
6166
}
@@ -66,7 +71,7 @@ export class TypeScriptFileRefactor {
6671
}
6772
diagnostics = diagnostics.concat(
6873
this._program.getSyntacticDiagnostics(this._sourceFile),
69-
this._program.getSemanticDiagnostics(this._sourceFile));
74+
typeCheck ? this._program.getSemanticDiagnostics(this._sourceFile) : []);
7075

7176
return diagnostics;
7277
}

tests/e2e/assets/webpack/test-app-weird/not/so/source/app/app.component.html

+8
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,11 @@ <h1>hello world</h1>
33
<a [routerLink]="['lazy']">lazy</a>
44
<router-outlet></router-outlet>
55
</div>
6+
7+
<!-- @ifdef DEBUG -->
8+
<p>DEBUG_ONLY</p>
9+
<!-- @endif -->
10+
11+
<!-- @ifndef DEBUG -->
12+
<p>PRODUCTION_ONLY</p>
13+
<!-- @endif -->
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
11
:host {
22
background-color: blue;
33
}
4+
5+
// @ifdef DEBUG
6+
:host::before {
7+
content: 'DEBUG_ONLY';
8+
}
9+
// @endif
10+
11+
// @ifndef DEBUG
12+
:host::before {
13+
content: 'PRODUCTION_ONLY';
14+
}
15+
// @endif

tests/e2e/assets/webpack/test-app-weird/not/so/source/app/app.module.ts

+9
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@ import { AppComponent } from './app.component';
1010
export class HomeView {}
1111

1212

13+
// @ifdef DEBUG
14+
console.log("DEBUG_ONLY");
15+
// @endif
16+
17+
// @ifndef DEBUG
18+
console.log("PRODUCTION_ONLY");
19+
// @endif
20+
21+
1322
@NgModule({
1423
declarations: [
1524
AppComponent,

tests/e2e/assets/webpack/test-app-weird/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"devDependencies": {
2020
"node-sass": "^3.7.0",
2121
"performance-now": "^0.2.0",
22+
"preprocess-loader": "^0.2.2",
2223
"raw-loader": "^0.5.1",
2324
"sass-loader": "^3.2.0",
2425
"typescript": "~2.1.0",

tests/e2e/assets/webpack/test-app-weird/webpack.config.js

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
const ngToolsWebpack = require('@ngtools/webpack');
22

3+
const flags = require('./webpack.flags.json');
4+
5+
const preprocessLoader = 'preprocess-loader' + (flags.DEBUG ? '?+DEBUG' : '');
6+
7+
38
module.exports = {
49
resolve: {
510
extensions: ['.ts', '.js']
@@ -15,10 +20,14 @@ module.exports = {
1520
],
1621
module: {
1722
loaders: [
18-
{ test: /\.scss$/, loaders: ['raw-loader', 'sass-loader'] },
23+
{ test: /\.scss$/, loaders: ['raw-loader', 'sass-loader', preprocessLoader] },
1924
{ test: /\.css$/, loader: 'raw-loader' },
20-
{ test: /\.html$/, loader: 'raw-loader' },
21-
{ test: /\.ts$/, loader: '@ngtools/webpack' }
25+
{ test: /\.html$/, loaders: ['raw-loader', preprocessLoader] },
26+
// Use preprocess to remove DEBUG only code.
27+
{ test: /\.ts$/, use: [
28+
{ loader: '@ngtools/webpack' },
29+
{ loader: preprocessLoader }
30+
] }
2231
]
2332
},
2433
devServer: {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"DEBUG": false
3+
}

tests/e2e/tests/packages/webpack/weird.ts

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import {normalize} from 'path';
22

33
import {createProjectFromAsset} from '../../../utils/assets';
4-
import {exec} from '../../../utils/process';
4+
import {exec, execWithEnv} from '../../../utils/process';
55
import {updateJsonFile} from '../../../utils/project';
6-
import {expectFileSizeToBeUnder, expectFileToExist} from '../../../utils/fs';
6+
import {expectFileSizeToBeUnder, expectFileToExist, expectFileToMatch} from '../../../utils/fs';
77
import {expectToFail} from '../../../utils/utils';
88

99

@@ -18,16 +18,28 @@ export default function(skipCleaning: () => void) {
1818
.then(() => expectFileSizeToBeUnder('dist/app.main.js', 410000))
1919
.then(() => expectFileSizeToBeUnder('dist/0.app.main.js', 40000))
2020

21+
// Verify that we're using the production environment.
22+
.then(() => expectFileToMatch('dist/app.main.js', /PRODUCTION_ONLY/))
23+
.then(() => expectToFail(() => expectFileToMatch('dist/app.main.js', /DEBUG_ONLY/)))
24+
2125
// Skip code generation and rebuild.
2226
.then(() => updateJsonFile('aotplugin.config.json', json => {
2327
json['skipCodeGeneration'] = true;
2428
}))
29+
.then(() => updateJsonFile('webpack.flags.json', json => {
30+
json['DEBUG'] = true;
31+
}))
2532
.then(() => exec(normalize('node_modules/.bin/webpack'), '-p'))
2633
.then(() => expectFileToExist('dist/app.main.js'))
2734
.then(() => expectFileToExist('dist/0.app.main.js'))
2835
.then(() => expectFileToExist('dist/1.app.main.js'))
2936
.then(() => expectFileToExist('dist/2.app.main.js'))
3037
.then(() => expectToFail(() => expectFileSizeToBeUnder('dist/app.main.js', 410000)))
3138
.then(() => expectFileSizeToBeUnder('dist/0.app.main.js', 40000))
39+
40+
// Verify that we're using the debug environment now.
41+
.then(() => expectFileToMatch('dist/app.main.js', /DEBUG_ONLY/))
42+
.then(() => expectToFail(() => expectFileToMatch('dist/app.main.js', /PRODUCTION_ONLY/)))
43+
3244
.then(() => skipCleaning());
3345
}

0 commit comments

Comments
 (0)