Skip to content

Commit ba973d4

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 8da9bcc commit ba973d4

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
@@ -422,15 +422,20 @@ function _diagnoseDeps(reasons: ModuleReason[], plugin: AotPlugin, checked: Set<
422422

423423

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

429429
const plugin = this._compilation._ngToolsWebpackPluginInstance as AotPlugin;
430430
// We must verify that AotPlugin is an instance of the right class.
431431
if (plugin && plugin instanceof AotPlugin) {
432+
if (plugin.compilerHost.readFile(sourceFileName) == source) {
433+
// In the case where the source is the same as the one in compilerHost, we don't have
434+
// extra TS loaders and there's no need to do any trickery.
435+
source = null;
436+
}
432437
const refactor = new TypeScriptFileRefactor(
433-
sourceFileName, plugin.compilerHost, plugin.program);
438+
sourceFileName, plugin.compilerHost, plugin.program, source);
434439

435440
Promise.resolve()
436441
.then(() => {
@@ -462,6 +467,25 @@ export function ngcLoader(this: LoaderContext & { _compilation: any }) {
462467
});
463468
})
464469
.then(() => {
470+
if (source) {
471+
// We need to validate diagnostics. We ignore type checking though, to save time.
472+
const diagnostics = refactor.getDiagnostics(false);
473+
if (diagnostics.length) {
474+
let message = '';
475+
476+
diagnostics.forEach(diagnostic => {
477+
const position = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
478+
479+
const fileName = diagnostic.file.fileName;
480+
const {line, character} = position;
481+
482+
const messageText = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
483+
message += `${fileName} (${line + 1},${character + 1}): ${messageText}\n`;
484+
});
485+
throw new Error(message);
486+
}
487+
}
488+
465489
// Force a few compiler options to make sure we get the result we want.
466490
const compilerOptions: ts.CompilerOptions = Object.assign({}, plugin.compilerOptions, {
467491
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)