Skip to content

Commit 22c1cb6

Browse files
alan-agius4angular-robot[bot]
authored andcommitted
fix(@angular-devkit/schematics): do not generate an UpdateBuffer for created and overridden files
`UpdateBuffer` only supports UTF-8 encoded files, which causes schematics to emit corrupted binary like files such as images. This commit also introduce an errors when the `UpdateRecorder` is used for non UTF-8 files. Closes #25174
1 parent 7dc0049 commit 22c1cb6

File tree

6 files changed

+30
-24
lines changed

6 files changed

+30
-24
lines changed

goldens/public-api/angular_devkit/schematics/index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -495,13 +495,13 @@ export class HostSink extends SimpleSinkBase {
495495
// (undocumented)
496496
_done(): Observable<void>;
497497
// (undocumented)
498-
protected _filesToCreate: Map<Path, UpdateBufferBase>;
498+
protected _filesToCreate: Map<Path, Buffer>;
499499
// (undocumented)
500500
protected _filesToDelete: Set<Path>;
501501
// (undocumented)
502502
protected _filesToRename: Set<[Path, Path]>;
503503
// (undocumented)
504-
protected _filesToUpdate: Map<Path, UpdateBufferBase>;
504+
protected _filesToUpdate: Map<Path, Buffer>;
505505
// (undocumented)
506506
protected _force: boolean;
507507
// (undocumented)

packages/angular/pwa/pwa/index.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -167,12 +167,7 @@ export default function (options: PwaOptions): Rule {
167167
return chain([
168168
externalSchematic('@schematics/angular', 'service-worker', swOptions),
169169
mergeWith(apply(url('./files/root'), [template({ ...options }), move(sourcePath)])),
170-
mergeWith(
171-
apply(url('./files/assets'), [
172-
template({ ...options }),
173-
move(posix.join(sourcePath, 'assets')),
174-
]),
175-
),
170+
mergeWith(apply(url('./files/assets'), [move(posix.join(sourcePath, 'assets'))])),
176171
...[...indexFiles].map((path) => updateIndexFile(path)),
177172
]);
178173
};

packages/angular_devkit/schematics/src/sink/dryrun.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,10 @@ export class DryRunSink extends HostSink {
117117
return;
118118
}
119119

120-
this._subject.next({ kind: 'create', path, content: content.generate() });
120+
this._subject.next({ kind: 'create', path, content });
121121
});
122122
this._filesToUpdate.forEach((content, path) => {
123-
this._subject.next({ kind: 'update', path, content: content.generate() });
123+
this._subject.next({ kind: 'update', path, content });
124124
});
125125

126126
this._subject.complete();

packages/angular_devkit/schematics/src/sink/host.ts

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,13 @@ import {
1717
reduce,
1818
} from 'rxjs';
1919
import { CreateFileAction } from '../tree/action';
20-
import { UpdateBufferBase } from '../utility/update-buffer';
2120
import { SimpleSinkBase } from './sink';
2221

2322
export class HostSink extends SimpleSinkBase {
2423
protected _filesToDelete = new Set<Path>();
2524
protected _filesToRename = new Set<[Path, Path]>();
26-
protected _filesToCreate = new Map<Path, UpdateBufferBase>();
27-
protected _filesToUpdate = new Map<Path, UpdateBufferBase>();
25+
protected _filesToCreate = new Map<Path, Buffer>();
26+
protected _filesToUpdate = new Map<Path, Buffer>();
2827

2928
constructor(protected _host: virtualFs.Host, protected _force = false) {
3029
super();
@@ -56,20 +55,23 @@ export class HostSink extends SimpleSinkBase {
5655
}
5756

5857
protected _overwriteFile(path: Path, content: Buffer): Observable<void> {
59-
this._filesToUpdate.set(path, UpdateBufferBase.create(content));
58+
this._filesToUpdate.set(path, content);
6059

6160
return EMPTY;
6261
}
62+
6363
protected _createFile(path: Path, content: Buffer): Observable<void> {
64-
this._filesToCreate.set(path, UpdateBufferBase.create(content));
64+
this._filesToCreate.set(path, content);
6565

6666
return EMPTY;
6767
}
68+
6869
protected _renameFile(from: Path, to: Path): Observable<void> {
6970
this._filesToRename.add([from, to]);
7071

7172
return EMPTY;
7273
}
74+
7375
protected _deleteFile(path: Path): Observable<void> {
7476
if (this._filesToCreate.has(path)) {
7577
this._filesToCreate.delete(path);
@@ -91,14 +93,10 @@ export class HostSink extends SimpleSinkBase {
9193
concatMap(([_, [path, to]]) => this._host.rename(path, to)),
9294
),
9395
observableFrom([...this._filesToCreate.entries()]).pipe(
94-
concatMap(([path, buffer]) => {
95-
return this._host.write(path, buffer.generate() as {} as virtualFs.FileBuffer);
96-
}),
96+
concatMap(([path, buffer]) => this._host.write(path, buffer)),
9797
),
9898
observableFrom([...this._filesToUpdate.entries()]).pipe(
99-
concatMap(([path, buffer]) => {
100-
return this._host.write(path, buffer.generate() as {} as virtualFs.FileBuffer);
101-
}),
99+
concatMap(([path, buffer]) => this._host.write(path, buffer)),
102100
),
103101
).pipe(reduce(() => {}));
104102
}

packages/angular_devkit/schematics/src/tree/recorder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export class UpdateRecorderBase implements UpdateRecorder {
1717

1818
constructor(entry: FileEntry) {
1919
this._original = Buffer.from(entry.content);
20-
this._content = UpdateBufferBase.create(entry.content);
20+
this._content = UpdateBufferBase.create(entry.path, entry.content);
2121
this._path = entry.path;
2222
}
2323

packages/angular_devkit/schematics/src/utility/update-buffer.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import { BaseException } from '@angular-devkit/core';
1010
import MagicString from 'magic-string';
11+
import { TextDecoder } from 'node:util';
1112

1213
export class IndexOutOfBoundException extends BaseException {
1314
constructor(index: number, min: number, max = Infinity) {
@@ -32,11 +33,23 @@ export abstract class UpdateBufferBase {
3233
/**
3334
* Creates an UpdateBufferBase instance.
3435
*
36+
* @param contentPath The path of the update buffer instance.
3537
* @param originalContent The original content of the update buffer instance.
3638
* @returns An UpdateBufferBase instance.
3739
*/
38-
static create(originalContent: Buffer): UpdateBufferBase {
39-
return new UpdateBuffer(originalContent);
40+
static create(contentPath: string, originalContent: Buffer): UpdateBufferBase {
41+
try {
42+
// We only support utf8 encoding.
43+
new TextDecoder('utf8', { fatal: true }).decode(originalContent);
44+
45+
return new UpdateBuffer(originalContent);
46+
} catch (e) {
47+
if (e instanceof TypeError) {
48+
throw new Error(`Failed to decode "${contentPath}" as UTF-8 text.`);
49+
}
50+
51+
throw e;
52+
}
4053
}
4154
}
4255

0 commit comments

Comments
 (0)