Alan Agius 9a3b63766a refactor(@angular-devkit/build-angular): remove redundant require.resolve for @angular/service-worker
This was mainly done prior to the introduction of optional peer dependencies.
2021-10-07 17:34:19 +02:00

140 lines
4.3 KiB
TypeScript

/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import { Path, getSystemPath, normalize } from '@angular-devkit/core';
import type { Config, Filesystem } from '@angular/service-worker/config';
import * as crypto from 'crypto';
import { createReadStream, promises as fs, constants as fsConstants } from 'fs';
import * as path from 'path';
import { pipeline } from 'stream';
import { loadEsmModule } from './load-esm';
class CliFilesystem implements Filesystem {
constructor(private base: string) {}
list(dir: string): Promise<string[]> {
return this._recursiveList(this._resolve(dir), []);
}
read(file: string): Promise<string> {
return fs.readFile(this._resolve(file), 'utf-8');
}
hash(file: string): Promise<string> {
return new Promise((resolve, reject) => {
const hash = crypto.createHash('sha1').setEncoding('hex');
pipeline(createReadStream(this._resolve(file)), hash, (error) =>
error ? reject(error) : resolve(hash.read()),
);
});
}
write(file: string, content: string): Promise<void> {
return fs.writeFile(this._resolve(file), content);
}
private _resolve(file: string): string {
return path.join(this.base, file);
}
private async _recursiveList(dir: string, items: string[]): Promise<string[]> {
const subdirectories = [];
for await (const entry of await fs.opendir(dir)) {
if (entry.isFile()) {
// Uses posix paths since the service worker expects URLs
items.push('/' + path.relative(this.base, path.join(dir, entry.name)).replace(/\\/g, '/'));
} else if (entry.isDirectory()) {
subdirectories.push(path.join(dir, entry.name));
}
}
for (const subdirectory of subdirectories) {
await this._recursiveList(subdirectory, items);
}
return items;
}
}
export async function augmentAppWithServiceWorker(
appRoot: Path,
outputPath: Path,
baseHref: string,
ngswConfigPath?: string,
): Promise<void> {
const distPath = getSystemPath(normalize(outputPath));
// Determine the configuration file path
const configPath = ngswConfigPath
? getSystemPath(normalize(ngswConfigPath))
: path.join(getSystemPath(appRoot), 'ngsw-config.json');
// Read the configuration file
let config: Config | undefined;
try {
const configurationData = await fs.readFile(configPath, 'utf-8');
config = JSON.parse(configurationData) as Config;
} catch (error) {
if (error.code === 'ENOENT') {
throw new Error(
'Error: Expected to find an ngsw-config.json configuration file' +
` in the ${getSystemPath(appRoot)} folder. Either provide one or` +
' disable Service Worker in the angular.json configuration file.',
);
} else {
throw error;
}
}
// Load ESM `@angular/service-worker/config` using the TypeScript dynamic import workaround.
// Once TypeScript provides support for keeping the dynamic import this workaround can be
// changed to a direct dynamic import.
const GeneratorConstructor = (
await loadEsmModule<typeof import('@angular/service-worker/config')>(
'@angular/service-worker/config',
)
).Generator;
// Generate the manifest
const generator = new GeneratorConstructor(new CliFilesystem(distPath), baseHref);
const output = await generator.process(config);
// Write the manifest
const manifest = JSON.stringify(output, null, 2);
await fs.writeFile(path.join(distPath, 'ngsw.json'), manifest);
// Find the service worker package
const workerPath = require.resolve('@angular/service-worker/ngsw-worker.js');
// Write the worker code
await fs.copyFile(
workerPath,
path.join(distPath, 'ngsw-worker.js'),
fsConstants.COPYFILE_FICLONE,
);
// If present, write the safety worker code
const safetyPath = path.join(path.dirname(workerPath), 'safety-worker.js');
try {
await fs.copyFile(
safetyPath,
path.join(distPath, 'worker-basic.min.js'),
fsConstants.COPYFILE_FICLONE,
);
await fs.copyFile(
safetyPath,
path.join(distPath, 'safety-worker.js'),
fsConstants.COPYFILE_FICLONE,
);
} catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
}
}