mirror of
https://github.com/angular/angular-cli.git
synced 2025-05-21 05:52:41 +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:
parent
584b51907c
commit
c2d2da41b1
@ -163,6 +163,7 @@ export function addModuleImportToStandaloneBootstrap(
|
|||||||
* @param functionName Name of the function that should be called.
|
* @param functionName Name of the function that should be called.
|
||||||
* @param importPath Path from which to import the function.
|
* @param importPath Path from which to import the function.
|
||||||
* @param args Arguments to use when calling the function.
|
* @param args Arguments to use when calling the function.
|
||||||
|
* @returns The file path that the provider was added to.
|
||||||
*/
|
*/
|
||||||
export function addFunctionalProvidersToStandaloneBootstrap(
|
export function addFunctionalProvidersToStandaloneBootstrap(
|
||||||
tree: Tree,
|
tree: Tree,
|
||||||
@ -170,7 +171,7 @@ export function addFunctionalProvidersToStandaloneBootstrap(
|
|||||||
functionName: string,
|
functionName: string,
|
||||||
importPath: string,
|
importPath: string,
|
||||||
args: ts.Expression[] = [],
|
args: ts.Expression[] = [],
|
||||||
) {
|
): string {
|
||||||
const sourceFile = createSourceFile(tree, filePath);
|
const sourceFile = createSourceFile(tree, filePath);
|
||||||
const bootstrapCall = findBootstrapApplicationCall(sourceFile);
|
const bootstrapCall = findBootstrapApplicationCall(sourceFile);
|
||||||
const addImports = (file: ts.SourceFile, recorder: UpdateRecorder) => {
|
const addImports = (file: ts.SourceFile, recorder: UpdateRecorder) => {
|
||||||
@ -198,7 +199,7 @@ export function addFunctionalProvidersToStandaloneBootstrap(
|
|||||||
addImports(sourceFile, recorder);
|
addImports(sourceFile, recorder);
|
||||||
tree.commitUpdate(recorder);
|
tree.commitUpdate(recorder);
|
||||||
|
|
||||||
return;
|
return filePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the config is a `mergeApplicationProviders` call, add another config to it.
|
// If the config is a `mergeApplicationProviders` call, add another config to it.
|
||||||
@ -208,7 +209,7 @@ export function addFunctionalProvidersToStandaloneBootstrap(
|
|||||||
addImports(sourceFile, recorder);
|
addImports(sourceFile, recorder);
|
||||||
tree.commitUpdate(recorder);
|
tree.commitUpdate(recorder);
|
||||||
|
|
||||||
return;
|
return filePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise attempt to merge into the current config.
|
// Otherwise attempt to merge into the current config.
|
||||||
@ -235,6 +236,8 @@ export function addFunctionalProvidersToStandaloneBootstrap(
|
|||||||
}
|
}
|
||||||
|
|
||||||
tree.commitUpdate(recorder);
|
tree.commitUpdate(recorder);
|
||||||
|
|
||||||
|
return configFilePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Finds the call to `bootstrapApplication` within a file. */
|
/** Finds the call to `bootstrapApplication` within a file. */
|
||||||
|
@ -20,12 +20,13 @@ import {
|
|||||||
url,
|
url,
|
||||||
} from '@angular-devkit/schematics';
|
} from '@angular-devkit/schematics';
|
||||||
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
|
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 * as ts from '../third_party/github.com/Microsoft/TypeScript/lib/typescript';
|
||||||
import { readWorkspace, writeWorkspace } from '../utility';
|
import { readWorkspace, writeWorkspace } from '../utility';
|
||||||
import { addSymbolToNgModuleMetadata, insertImport } from '../utility/ast-utils';
|
import { addSymbolToNgModuleMetadata, insertImport } from '../utility/ast-utils';
|
||||||
import { applyToUpdateRecorder } from '../utility/change';
|
import { applyToUpdateRecorder } from '../utility/change';
|
||||||
import { addPackageJsonDependency, getPackageJsonDependency } from '../utility/dependencies';
|
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 { relativePathToWorkspaceRoot } from '../utility/paths';
|
||||||
import { targetBuildNotFoundError } from '../utility/project-targets';
|
import { targetBuildNotFoundError } from '../utility/project-targets';
|
||||||
import { BrowserBuilderOptions } from '../utility/workspace-models';
|
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 {
|
function getTsSourceFile(host: Tree, path: string): ts.SourceFile {
|
||||||
const content = host.readText(path);
|
const content = host.readText(path);
|
||||||
const source = ts.createSourceFile(path, content, ts.ScriptTarget.Latest, true);
|
const source = ts.createSourceFile(path, content, ts.ScriptTarget.Latest, true);
|
||||||
@ -116,23 +155,25 @@ export default function (options: ServiceWorkerOptions): Rule {
|
|||||||
resourcesOutputPath = normalize(`/${resourcesOutputPath}`);
|
resourcesOutputPath = normalize(`/${resourcesOutputPath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const templateSource = apply(url('./files'), [
|
context.addTask(new NodePackageInstallTask());
|
||||||
|
|
||||||
|
await writeWorkspace(host, workspace);
|
||||||
|
|
||||||
|
const { main } = buildOptions;
|
||||||
|
|
||||||
|
return chain([
|
||||||
|
mergeWith(
|
||||||
|
apply(url('./files'), [
|
||||||
applyTemplates({
|
applyTemplates({
|
||||||
...options,
|
...options,
|
||||||
resourcesOutputPath,
|
resourcesOutputPath,
|
||||||
relativePathToWorkspaceRoot: relativePathToWorkspaceRoot(project.root),
|
relativePathToWorkspaceRoot: relativePathToWorkspaceRoot(project.root),
|
||||||
}),
|
}),
|
||||||
move(project.root),
|
move(project.root),
|
||||||
]);
|
]),
|
||||||
|
),
|
||||||
context.addTask(new NodePackageInstallTask());
|
|
||||||
|
|
||||||
await writeWorkspace(host, workspace);
|
|
||||||
|
|
||||||
return chain([
|
|
||||||
mergeWith(templateSource),
|
|
||||||
addDependencies(),
|
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
|
* 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 { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
|
||||||
import { Schema as ApplicationOptions } from '../application/schema';
|
import { Schema as ApplicationOptions } from '../application/schema';
|
||||||
import { Schema as WorkspaceOptions } from '../workspace/schema';
|
import { Schema as WorkspaceOptions } from '../workspace/schema';
|
||||||
@ -164,4 +165,56 @@ describe('Service Worker Schematic', () => {
|
|||||||
const { projects } = JSON.parse(tree.readContent('/angular.json'));
|
const { projects } = JSON.parse(tree.readContent('/angular.json'));
|
||||||
expect(projects.foo.architect.build.options.ngswConfigPath).toBe('ngsw-config.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';`);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user