Skip to content

Commit d592cad

Browse files
committed
refactor(@ngtools/webpack): use webpack input filesystem as default host
1 parent 043ecde commit d592cad

File tree

5 files changed

+143
-56
lines changed

5 files changed

+143
-56
lines changed

packages/ngtools/webpack/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
},
2727
"dependencies": {
2828
"@angular-devkit/core": "0.0.0",
29+
"rxjs": "^6.0.0",
2930
"tree-kill": "^1.0.0",
3031
"webpack-sources": "^1.1.0"
3132
},

packages/ngtools/webpack/src/angular_compiler_plugin.ts

+43-52
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88
import { Path, dirname, getSystemPath, normalize, resolve, virtualFs } from '@angular-devkit/core';
9-
import { NodeJsSyncHost } from '@angular-devkit/core/node';
109
import { ChildProcess, ForkOptions, fork } from 'child_process';
1110
import * as fs from 'fs';
1211
import * as path from 'path';
@@ -58,6 +57,7 @@ import {
5857
NodeWatchFileSystemInterface,
5958
NormalModuleFactoryRequest,
6059
} from './webpack';
60+
import { WebpackInputHost } from './webpack-input-host';
6161

6262
const treeKill = require('tree-kill');
6363

@@ -289,48 +289,6 @@ export class AngularCompilerPlugin {
289289
this._contextElementDependencyConstructor = options.contextElementDependencyConstructor
290290
|| require('webpack/lib/dependencies/ContextElementDependency');
291291

292-
293-
let host: virtualFs.Host<fs.Stats> = options.host || new NodeJsSyncHost();
294-
if (options.hostReplacementPaths) {
295-
if (typeof options.hostReplacementPaths == 'function') {
296-
const replacementResolver = options.hostReplacementPaths;
297-
host = new class extends virtualFs.ResolverHost<fs.Stats> {
298-
_resolve(path: Path) {
299-
return normalize(replacementResolver(getSystemPath(path)));
300-
}
301-
}(host);
302-
} else {
303-
const aliasHost = new virtualFs.AliasHost(host);
304-
for (const from in options.hostReplacementPaths) {
305-
aliasHost.aliases.set(normalize(from), normalize(options.hostReplacementPaths[from]));
306-
}
307-
host = aliasHost;
308-
}
309-
}
310-
311-
// Create the webpack compiler host.
312-
const webpackCompilerHost = new WebpackCompilerHost(
313-
this._compilerOptions,
314-
this._basePath,
315-
host,
316-
);
317-
webpackCompilerHost.enableCaching();
318-
319-
// Create and set a new WebpackResourceLoader.
320-
this._resourceLoader = new WebpackResourceLoader();
321-
webpackCompilerHost.setResourceLoader(this._resourceLoader);
322-
323-
// Use the WebpackCompilerHost with a resource loader to create an AngularCompilerHost.
324-
this._compilerHost = createCompilerHost({
325-
options: this._compilerOptions,
326-
tsHost: webpackCompilerHost,
327-
}) as CompilerHost & WebpackCompilerHost;
328-
329-
// Resolve mainPath if provided.
330-
if (options.mainPath) {
331-
this._mainPath = this._compilerHost.resolve(options.mainPath);
332-
}
333-
334292
// Use entryModule if available in options, otherwise resolve it from mainPath after program
335293
// creation.
336294
if (this._options.entryModule) {
@@ -621,28 +579,61 @@ export class AngularCompilerPlugin {
621579
watchFileSystem: NodeWatchFileSystemInterface,
622580
};
623581

624-
const inputDecorator = new VirtualFileSystemDecorator(
582+
let host: virtualFs.Host<fs.Stats> = this._options.host || new WebpackInputHost(
625583
compilerWithFileSystems.inputFileSystem,
626-
this._compilerHost,
627584
);
628-
compilerWithFileSystems.inputFileSystem = inputDecorator;
629585

630586
let replacements: Map<Path, Path> | ((path: Path) => Path) | undefined;
631587
if (this._options.hostReplacementPaths) {
632-
if (typeof this._options.hostReplacementPaths === 'function') {
588+
if (typeof this._options.hostReplacementPaths == 'function') {
633589
const replacementResolver = this._options.hostReplacementPaths;
634590
replacements = path => normalize(replacementResolver(getSystemPath(path)));
591+
host = new class extends virtualFs.ResolverHost<fs.Stats> {
592+
_resolve(path: Path) {
593+
return normalize(replacementResolver(getSystemPath(path)));
594+
}
595+
}(host);
635596
} else {
636597
replacements = new Map();
637-
for (const replace in this._options.hostReplacementPaths) {
638-
replacements.set(
639-
normalize(replace),
640-
normalize(this._options.hostReplacementPaths[replace]),
641-
);
598+
const aliasHost = new virtualFs.AliasHost(host);
599+
for (const from in this._options.hostReplacementPaths) {
600+
const normalizedFrom = normalize(from);
601+
const normalizedWith = normalize(this._options.hostReplacementPaths[from]);
602+
aliasHost.aliases.set(normalizedFrom, normalizedWith);
603+
replacements.set(normalizedFrom, normalizedWith);
642604
}
605+
host = aliasHost;
643606
}
644607
}
645608

609+
// Create the webpack compiler host.
610+
const webpackCompilerHost = new WebpackCompilerHost(
611+
this._compilerOptions,
612+
this._basePath,
613+
host,
614+
);
615+
webpackCompilerHost.enableCaching();
616+
617+
// Create and set a new WebpackResourceLoader.
618+
this._resourceLoader = new WebpackResourceLoader();
619+
webpackCompilerHost.setResourceLoader(this._resourceLoader);
620+
621+
// Use the WebpackCompilerHost with a resource loader to create an AngularCompilerHost.
622+
this._compilerHost = createCompilerHost({
623+
options: this._compilerOptions,
624+
tsHost: webpackCompilerHost,
625+
}) as CompilerHost & WebpackCompilerHost;
626+
627+
// Resolve mainPath if provided.
628+
if (this._options.mainPath) {
629+
this._mainPath = this._compilerHost.resolve(this._options.mainPath);
630+
}
631+
632+
const inputDecorator = new VirtualFileSystemDecorator(
633+
compilerWithFileSystems.inputFileSystem,
634+
this._compilerHost,
635+
);
636+
compilerWithFileSystems.inputFileSystem = inputDecorator;
646637
compilerWithFileSystems.watchFileSystem = new VirtualWatchFileSystemDecorator(
647638
inputDecorator,
648639
replacements,

packages/ngtools/webpack/src/virtual_file_system_decorator.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export class VirtualFileSystemDecorator implements InputFileSystem {
5252
this._inputFileSystem.readdir(path, callback);
5353
}
5454

55-
readFile(path: string, callback: Callback<string | Buffer>): void {
55+
readFile(path: string, callback: Callback<Buffer>): void {
5656
const result = this._readFileSync(path);
5757
if (result) {
5858
callback(null, result);
@@ -79,7 +79,7 @@ export class VirtualFileSystemDecorator implements InputFileSystem {
7979
return this._inputFileSystem.readdirSync(path);
8080
}
8181

82-
readFileSync(path: string): string | Buffer {
82+
readFileSync(path: string): Buffer {
8383
const result = this._readFileSync(path);
8484

8585
return result || this._inputFileSystem.readFileSync(path);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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 { Path, PathFragment, fragment, getSystemPath, virtualFs } from '@angular-devkit/core';
9+
import { Stats } from 'fs';
10+
import { Observable, throwError } from 'rxjs';
11+
import { map } from 'rxjs/operators';
12+
import { InputFileSystem } from './webpack';
13+
14+
// Host is used instead of ReadonlyHost due to most decorators only supporting Hosts
15+
export class WebpackInputHost implements virtualFs.Host<Stats> {
16+
17+
constructor(public readonly inputFileSystem: InputFileSystem) { }
18+
19+
get capabilities(): virtualFs.HostCapabilities {
20+
return { synchronous: true };
21+
}
22+
23+
write(_path: Path, _content: virtualFs.FileBufferLike) {
24+
return throwError(new Error('Not supported.'));
25+
}
26+
27+
delete(_path: Path) {
28+
return throwError(new Error('Not supported.'));
29+
}
30+
31+
rename(_from: Path, _to: Path) {
32+
return throwError(new Error('Not supported.'));
33+
}
34+
35+
read(path: Path): Observable<virtualFs.FileBuffer> {
36+
return new Observable(obs => {
37+
// TODO: remove this try+catch when issue https://github.com/ReactiveX/rxjs/issues/3740 is
38+
// fixed.
39+
try {
40+
const data = this.inputFileSystem.readFileSync(getSystemPath(path));
41+
obs.next(new Uint8Array(data).buffer as ArrayBuffer);
42+
obs.complete();
43+
} catch (e) {
44+
obs.error(e);
45+
}
46+
});
47+
}
48+
49+
list(path: Path): Observable<PathFragment[]> {
50+
return new Observable(obs => {
51+
// TODO: remove this try+catch when issue https://github.com/ReactiveX/rxjs/issues/3740 is
52+
// fixed.
53+
try {
54+
const names = this.inputFileSystem.readdirSync(getSystemPath(path));
55+
obs.next(names.map(name => fragment(name)));
56+
obs.complete();
57+
} catch (err) {
58+
obs.error(err);
59+
}
60+
});
61+
}
62+
63+
exists(path: Path): Observable<boolean> {
64+
return this.stat(path).pipe(map(stats => stats != null));
65+
}
66+
67+
isDirectory(path: Path): Observable<boolean> {
68+
return this.stat(path).pipe(map(stats => stats != null && stats.isDirectory()));
69+
}
70+
71+
isFile(path: Path): Observable<boolean> {
72+
return this.stat(path).pipe(map(stats => stats != null && stats.isFile()));
73+
}
74+
75+
stat(path: Path): Observable<Stats | null> {
76+
return new Observable(obs => {
77+
try {
78+
const stats = this.inputFileSystem.statSync(getSystemPath(path));
79+
obs.next(stats);
80+
obs.complete();
81+
} catch (e) {
82+
if (e.code === 'ENOENT') {
83+
obs.next(null);
84+
obs.complete();
85+
} else {
86+
obs.error(e);
87+
}
88+
}
89+
});
90+
}
91+
92+
watch(_path: Path, _options?: virtualFs.HostWatchOptions): null {
93+
return null;
94+
}
95+
}

packages/ngtools/webpack/src/webpack.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ export interface NormalModuleFactoryRequest {
2222
export interface InputFileSystem {
2323
stat(path: string, callback: Callback<Stats>): void;
2424
readdir(path: string, callback: Callback<string[]>): void;
25-
readFile(path: string, callback: Callback<string | Buffer>): void;
25+
readFile(path: string, callback: Callback<Buffer>): void;
2626
readJson(path: string, callback: Callback): void;
2727
readlink(path: string, callback: Callback<string>): void;
2828
statSync(path: string): Stats;
2929
readdirSync(path: string): string[];
30-
readFileSync(path: string): string | Buffer;
30+
readFileSync(path: string): Buffer;
3131
// tslint:disable-next-line:no-any
3232
readJsonSync(path: string): any;
3333
readlinkSync(path: string): string;

0 commit comments

Comments
 (0)