1
0
mirror of https://github.com/angular/angular-cli.git synced 2025-05-20 13:32:43 +08:00

feat(@schematics/angular): add support to add service worker to standalone application

This commit adds support to generate a service worker in a standalone application.
This commit is contained in:
Alan Agius 2023-04-04 14:25:18 +00:00 committed by angular-robot[bot]
parent 584b51907c
commit c2d2da41b1
3 changed files with 112 additions and 15 deletions
packages/schematics/angular

@ -163,6 +163,7 @@ export function addModuleImportToStandaloneBootstrap(
* @param functionName Name of the function that should be called.
* @param importPath Path from which to import the function.
* @param args Arguments to use when calling the function.
* @returns The file path that the provider was added to.
*/
export function addFunctionalProvidersToStandaloneBootstrap(
tree: Tree,
@ -170,7 +171,7 @@ export function addFunctionalProvidersToStandaloneBootstrap(
functionName: string,
importPath: string,
args: ts.Expression[] = [],
) {
): string {
const sourceFile = createSourceFile(tree, filePath);
const bootstrapCall = findBootstrapApplicationCall(sourceFile);
const addImports = (file: ts.SourceFile, recorder: UpdateRecorder) => {
@ -198,7 +199,7 @@ export function addFunctionalProvidersToStandaloneBootstrap(
addImports(sourceFile, recorder);
tree.commitUpdate(recorder);
return;
return filePath;
}
// If the config is a `mergeApplicationProviders` call, add another config to it.
@ -208,7 +209,7 @@ export function addFunctionalProvidersToStandaloneBootstrap(
addImports(sourceFile, recorder);
tree.commitUpdate(recorder);
return;
return filePath;
}
// Otherwise attempt to merge into the current config.
@ -235,6 +236,8 @@ export function addFunctionalProvidersToStandaloneBootstrap(
}
tree.commitUpdate(recorder);
return configFilePath;
}
/** Finds the call to `bootstrapApplication` within a file. */

@ -20,12 +20,13 @@ import {
url,
} from '@angular-devkit/schematics';
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
import { addFunctionalProvidersToStandaloneBootstrap } from '../private/standalone';
import * as ts from '../third_party/github.com/Microsoft/TypeScript/lib/typescript';
import { readWorkspace, writeWorkspace } from '../utility';
import { addSymbolToNgModuleMetadata, insertImport } from '../utility/ast-utils';
import { applyToUpdateRecorder } from '../utility/change';
import { addPackageJsonDependency, getPackageJsonDependency } from '../utility/dependencies';
import { getAppModulePath } from '../utility/ng-ast-utils';
import { getAppModulePath, isStandaloneApp } from '../utility/ng-ast-utils';
import { relativePathToWorkspaceRoot } from '../utility/paths';
import { targetBuildNotFoundError } from '../utility/project-targets';
import { BrowserBuilderOptions } from '../utility/workspace-models';
@ -85,6 +86,44 @@ function updateAppModule(mainPath: string): Rule {
};
}
function addProvideServiceWorker(mainPath: string): Rule {
return (host: Tree) => {
const updatedFilePath = addFunctionalProvidersToStandaloneBootstrap(
host,
mainPath,
'provideServiceWorker',
'@angular/service-worker',
[
ts.factory.createStringLiteral('ngsw-worker.js', true),
ts.factory.createObjectLiteralExpression(
[
ts.factory.createPropertyAssignment(
ts.factory.createIdentifier('enabled'),
ts.factory.createPrefixUnaryExpression(
ts.SyntaxKind.ExclamationToken,
ts.factory.createCallExpression(
ts.factory.createIdentifier('isDevMode'),
undefined,
[],
),
),
),
ts.factory.createPropertyAssignment(
ts.factory.createIdentifier('registrationStrategy'),
ts.factory.createStringLiteral('registerWhenStable:30000', true),
),
],
true,
),
],
);
addImport(host, updatedFilePath, 'isDevMode', '@angular/core');
return host;
};
}
function getTsSourceFile(host: Tree, path: string): ts.SourceFile {
const content = host.readText(path);
const source = ts.createSourceFile(path, content, ts.ScriptTarget.Latest, true);
@ -116,23 +155,25 @@ export default function (options: ServiceWorkerOptions): Rule {
resourcesOutputPath = normalize(`/${resourcesOutputPath}`);
}
const templateSource = apply(url('./files'), [
applyTemplates({
...options,
resourcesOutputPath,
relativePathToWorkspaceRoot: relativePathToWorkspaceRoot(project.root),
}),
move(project.root),
]);
context.addTask(new NodePackageInstallTask());
await writeWorkspace(host, workspace);
const { main } = buildOptions;
return chain([
mergeWith(templateSource),
mergeWith(
apply(url('./files'), [
applyTemplates({
...options,
resourcesOutputPath,
relativePathToWorkspaceRoot: relativePathToWorkspaceRoot(project.root),
}),
move(project.root),
]),
),
addDependencies(),
updateAppModule(buildOptions.main),
isStandaloneApp(host, main) ? addProvideServiceWorker(main) : updateAppModule(main),
]);
};
}

@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import { tags } from '@angular-devkit/core';
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
import { Schema as ApplicationOptions } from '../application/schema';
import { Schema as WorkspaceOptions } from '../workspace/schema';
@ -164,4 +165,56 @@ describe('Service Worker Schematic', () => {
const { projects } = JSON.parse(tree.readContent('/angular.json'));
expect(projects.foo.architect.build.options.ngswConfigPath).toBe('ngsw-config.json');
});
describe('standalone', () => {
const name = 'buz';
const standaloneAppOptions: ApplicationOptions = {
...appOptions,
name,
standalone: true,
};
const standaloneSWOptions: ServiceWorkerOptions = {
...defaultOptions,
project: name,
};
beforeEach(async () => {
appTree = await schematicRunner.runSchematic('application', standaloneAppOptions, appTree);
});
it(`should add the 'provideServiceWorker' to providers`, async () => {
const tree = await schematicRunner.runSchematic(
'service-worker',
standaloneSWOptions,
appTree,
);
const content = tree.readContent('/projects/buz/src/app/app.config.ts');
expect(tags.oneLine`${content}`).toContain(tags.oneLine`
providers: [provideServiceWorker('ngsw-worker.js', {
enabled: !isDevMode(),
registrationStrategy: 'registerWhenStable:30000'
})]
`);
});
it(`should import 'isDevMode' from '@angular/core'`, async () => {
const tree = await schematicRunner.runSchematic(
'service-worker',
standaloneSWOptions,
appTree,
);
const content = tree.readContent('/projects/buz/src/app/app.config.ts');
expect(content).toContain(`import { ApplicationConfig, isDevMode } from '@angular/core';`);
});
it(`should import 'provideServiceWorker' from '@angular/service-worker'`, async () => {
const tree = await schematicRunner.runSchematic(
'service-worker',
standaloneSWOptions,
appTree,
);
const content = tree.readContent('/projects/buz/src/app/app.config.ts');
expect(content).toContain(`import { provideServiceWorker } from '@angular/service-worker';`);
});
});
});