diff --git a/packages/ngtools/webpack/src/compiler_host.ts b/packages/ngtools/webpack/src/compiler_host.ts index 7ed4d444fe..e11acf71fe 100644 --- a/packages/ngtools/webpack/src/compiler_host.ts +++ b/packages/ngtools/webpack/src/compiler_host.ts @@ -74,25 +74,61 @@ export class VirtualDirStats extends VirtualStats { export class VirtualFileStats extends VirtualStats { private _sourceFile: ts.SourceFile | null; - constructor(_fileName: string, private _content: string) { + private _content: string | null; + private _bufferContent: virtualFs.FileBuffer | null; + + constructor(_fileName: string) { super(_fileName); } - get content() { return this._content; } + static createFromString(_fileName: string, _content: string) { + const stats = new VirtualFileStats(_fileName); + stats.content = _content; + + return stats; + } + + static createFromBuffer(_fileName: string, _buffer: virtualFs.FileBuffer) { + const stats = new VirtualFileStats(_fileName); + stats.bufferContent = _buffer; + + return stats; + } + + get content() { + if (!this._content && this.bufferContent) { + this._content = virtualFs.fileBufferToString(this.bufferContent); + } + + return this._content || ''; + } set content(v: string) { this._content = v; - this._mtime = new Date(); - this._sourceFile = null; + this._bufferContent = null; + this.resetMetadata(); + } + + get bufferContent() { + if (!this._bufferContent && this._content) { + this._bufferContent = virtualFs.stringToFileBuffer(this._content); + } + + return this._bufferContent || virtualFs.stringToFileBuffer(''); + } + set bufferContent(buf: virtualFs.FileBuffer) { + this._bufferContent = buf; + this._content = null; + this.resetMetadata(); } + setSourceFile(sourceFile: ts.SourceFile) { this._sourceFile = sourceFile; } getSourceFile(languageVersion: ts.ScriptTarget, setParentNodes: boolean) { if (!this._sourceFile) { - // console.log(this._path) this._sourceFile = ts.createSourceFile( workaroundResolve(this._path), - this._content, + this.content, languageVersion, setParentNodes); } @@ -100,12 +136,16 @@ export class VirtualFileStats extends VirtualStats { return this._sourceFile; } + private resetMetadata(): void { + this._mtime = new Date(); + this._sourceFile = null; + } + isFile() { return true; } - get size() { return this._content.length; } + get size() { return this.content.length; } } - export class WebpackCompilerHost implements ts.CompilerHost { private _syncHost: virtualFs.SyncDelegateHost; private _files: {[path: string]: VirtualFileStats | null} = Object.create(null); @@ -149,8 +189,8 @@ export class WebpackCompilerHost implements ts.CompilerHost { } } - private _setFileContent(fileName: Path, content: string) { - this._files[fileName] = new VirtualFileStats(fileName, content); + private _cacheFile(fileName: string, stats: VirtualFileStats) { + this._files[fileName] = stats; let p = dirname(fileName); while (p && !this._directories[p]) { @@ -211,33 +251,48 @@ export class WebpackCompilerHost implements ts.CompilerHost { } readFile(fileName: string): string | undefined { + const stats = this.findVirtualFile(fileName); + + return stats && stats.content; + } + + readFileBuffer(fileName: string): Buffer | undefined { + const stats = this.findVirtualFile(fileName); + if (stats) { + const buffer = Buffer.from(stats.bufferContent); + + return buffer; + } + } + + private findVirtualFile(fileName: string): VirtualFileStats | undefined { const p = this.resolve(fileName); const stats = this._files[p]; - if (!stats) { - try { - const result = virtualFs.fileBufferToString(this._syncHost.read(p)); - if (result !== undefined) { - if (this._cache) { - this._setFileContent(p, result); - } + if (stats) { + return stats; + } + + try { + const fileBuffer = this._syncHost.read(p); + if (fileBuffer) { + const stats = VirtualFileStats.createFromBuffer(p, fileBuffer); + if (this._cache) { + this._cacheFile(p, stats); } - return result; - } catch (e) { - return undefined; + return stats; } + } catch (e) { + return undefined; } - - return stats.content; } - // Does not delegate, use with `fileExists/directoryExists()`. - stat(path: string): VirtualStats { + stat(path: string): VirtualStats | null { const p = this.resolve(path); const stats = this._files[p] || this._directories[p]; if (!stats) { - throw new Error(`File not found: ${JSON.stringify(p)}`); + return this._syncHost.stat(p) as VirtualStats | null; } return stats; @@ -345,7 +400,8 @@ export class WebpackCompilerHost implements ts.CompilerHost { _sourceFiles?: ReadonlyArray, ): void => { const p = this.resolve(fileName); - this._setFileContent(p, data); + const stats = VirtualFileStats.createFromString(p, data); + this._cacheFile(p, stats); }; } diff --git a/packages/ngtools/webpack/src/virtual_file_system_decorator.ts b/packages/ngtools/webpack/src/virtual_file_system_decorator.ts index e7b4cb049f..205a87e2c3 100644 --- a/packages/ngtools/webpack/src/virtual_file_system_decorator.ts +++ b/packages/ngtools/webpack/src/virtual_file_system_decorator.ts @@ -18,17 +18,16 @@ export class VirtualFileSystemDecorator implements InputFileSystem { private _webpackCompilerHost: WebpackCompilerHost, ) { } - // We only need to intercept calls to individual files that are present in WebpackCompilerHost. - private _readFileSync(path: string): string | null { - if (this._webpackCompilerHost.fileExists(path, false)) { - return this._webpackCompilerHost.readFile(path) || null; + private _readFileSync(path: string): Buffer | null { + if (this._webpackCompilerHost.fileExists(path)) { + return this._webpackCompilerHost.readFileBuffer(path) || null; } return null; } private _statSync(path: string): Stats | null { - if (this._webpackCompilerHost.fileExists(path, false)) { + if (this._webpackCompilerHost.fileExists(path)) { return this._webpackCompilerHost.stat(path); } @@ -52,7 +51,7 @@ export class VirtualFileSystemDecorator implements InputFileSystem { this._inputFileSystem.readdir(path, callback); } - readFile(path: string, callback: Callback): void { + readFile(path: string, callback: Callback): void { const result = this._readFileSync(path); if (result) { callback(null, result); @@ -79,7 +78,7 @@ export class VirtualFileSystemDecorator implements InputFileSystem { return this._inputFileSystem.readdirSync(path); } - readFileSync(path: string): string { + readFileSync(path: string): string | Buffer { const result = this._readFileSync(path); return result || this._inputFileSystem.readFileSync(path); diff --git a/packages/ngtools/webpack/src/webpack.ts b/packages/ngtools/webpack/src/webpack.ts index 660f39edf5..91169a791c 100644 --- a/packages/ngtools/webpack/src/webpack.ts +++ b/packages/ngtools/webpack/src/webpack.ts @@ -51,13 +51,13 @@ export interface NormalModuleFactoryRequest { export interface InputFileSystem { stat(path: string, callback: Callback): void; readdir(path: string, callback: Callback): void; - readFile(path: string, callback: Callback): void; + readFile(path: string, callback: Callback): void; // tslint:disable-next-line:no-any readJson(path: string, callback: Callback): void; readlink(path: string, callback: Callback): void; statSync(path: string): Stats; readdirSync(path: string): string[]; - readFileSync(path: string): string; + readFileSync(path: string): string | Buffer; // tslint:disable-next-line:no-any readJsonSync(path: string): any; readlinkSync(path: string): string;