Skip to content

Commit

Permalink
feat(@schematics/angular): add support to add service worker to stand…
Browse files Browse the repository at this point in the history
…alone application

This commit adds support to generate a service worker in a standalone application.
  • Loading branch information
alan-agius4 authored and angular-robot[bot] committed Apr 4, 2023
1 parent 584b519 commit c2d2da4
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 15 deletions.
9 changes: 6 additions & 3 deletions packages/schematics/angular/private/standalone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,14 +163,15 @@ 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,
filePath: string,
functionName: string,
importPath: string,
args: ts.Expression[] = [],
) {
): string {
const sourceFile = createSourceFile(tree, filePath);
const bootstrapCall = findBootstrapApplicationCall(sourceFile);
const addImports = (file: ts.SourceFile, recorder: UpdateRecorder) => {
Expand Down Expand Up @@ -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.
Expand All @@ -208,7 +209,7 @@ export function addFunctionalProvidersToStandaloneBootstrap(
addImports(sourceFile, recorder);
tree.commitUpdate(recorder);

return;
return filePath;
}

// Otherwise attempt to merge into the current config.
Expand All @@ -235,6 +236,8 @@ export function addFunctionalProvidersToStandaloneBootstrap(
}

tree.commitUpdate(recorder);

return configFilePath;
}

/** Finds the call to `bootstrapApplication` within a file. */
Expand Down
65 changes: 53 additions & 12 deletions packages/schematics/angular/service-worker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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),
]);
};
}
Expand Down
53 changes: 53 additions & 0 deletions packages/schematics/angular/service-worker/index_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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';`);
});
});
});

0 comments on commit c2d2da4

Please sign in to comment.