mirror of
https://github.com/angular/angular-cli.git
synced 2025-05-17 11:03:53 +08:00
138 lines
4.4 KiB
TypeScript
138 lines
4.4 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google Inc. 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 { Rule } from '@angular-devkit/schematics';
|
|
import * as ts from '../../third_party/github.com/Microsoft/TypeScript/lib/typescript';
|
|
import { findNodes } from '../../utility/ast-utils';
|
|
import { allWorkspaceTargets, getWorkspace } from '../../utility/workspace';
|
|
import { Builders } from '../../utility/workspace-models';
|
|
|
|
/**
|
|
* Update the `main.server.ts` file by adding exports to `renderModule` and `renderModuleFactory` which are
|
|
* now required for Universal and App-Shell for Ivy and `bundleDependencies`.
|
|
*/
|
|
export function updateServerMainFile(): Rule {
|
|
return async tree => {
|
|
const workspace = await getWorkspace(tree);
|
|
|
|
for (const [targetName, target] of allWorkspaceTargets(workspace)) {
|
|
if (targetName !== 'server' || target.builder !== Builders.Server) {
|
|
continue;
|
|
}
|
|
|
|
// find the main server file
|
|
const mainFilePath = target.options?.main;
|
|
if (!mainFilePath || typeof mainFilePath !== 'string') {
|
|
continue;
|
|
}
|
|
|
|
const content = tree.read(mainFilePath);
|
|
if (!content) {
|
|
continue;
|
|
}
|
|
|
|
const source = ts.createSourceFile(
|
|
mainFilePath,
|
|
content.toString().replace(/^\uFEFF/, ''),
|
|
ts.ScriptTarget.Latest,
|
|
true,
|
|
);
|
|
|
|
// find exports in main server file
|
|
const exportDeclarations = findNodes(source, ts.SyntaxKind.ExportDeclaration) as ts.ExportDeclaration[];
|
|
|
|
const platformServerExports = exportDeclarations.filter(({ moduleSpecifier }) => (
|
|
moduleSpecifier && ts.isStringLiteral(moduleSpecifier) && moduleSpecifier.text === '@angular/platform-server'
|
|
));
|
|
|
|
let hasRenderModule = false;
|
|
let hasRenderModuleFactory = false;
|
|
|
|
// find exports of renderModule or renderModuleFactory
|
|
for (const { exportClause } of platformServerExports) {
|
|
if (exportClause && ts.isNamedExports(exportClause)) {
|
|
if (!hasRenderModuleFactory) {
|
|
hasRenderModuleFactory = exportClause.elements.some(({ name }) => name.text === 'renderModuleFactory');
|
|
}
|
|
|
|
if (!hasRenderModule) {
|
|
hasRenderModule = exportClause.elements.some(({ name }) => name.text === 'renderModule');
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hasRenderModule && hasRenderModuleFactory) {
|
|
// We have both required exports
|
|
continue;
|
|
}
|
|
|
|
let exportSpecifiers: ts.ExportSpecifier[] = [];
|
|
let updateExisting = false;
|
|
|
|
// Add missing exports
|
|
if (platformServerExports.length) {
|
|
const { exportClause } = platformServerExports[0] as ts.ExportDeclaration;
|
|
if (!exportClause || ts.isNamespaceExport(exportClause)) {
|
|
continue;
|
|
}
|
|
|
|
exportSpecifiers = [...exportClause.elements];
|
|
updateExisting = true;
|
|
}
|
|
|
|
if (!hasRenderModule) {
|
|
exportSpecifiers.push(ts.createExportSpecifier(
|
|
undefined,
|
|
ts.createIdentifier('renderModule'),
|
|
));
|
|
}
|
|
|
|
if (!hasRenderModuleFactory) {
|
|
exportSpecifiers.push(ts.createExportSpecifier(
|
|
undefined,
|
|
ts.createIdentifier('renderModuleFactory'),
|
|
));
|
|
}
|
|
|
|
// Create a TS printer to get the text of the export node
|
|
const printer = ts.createPrinter();
|
|
|
|
const moduleSpecifier = ts.createStringLiteral('@angular/platform-server');
|
|
|
|
// TypeScript will emit the Node with double quotes.
|
|
// In schematics we usually write code with a single quotes
|
|
// tslint:disable-next-line: no-any
|
|
(moduleSpecifier as any).singleQuote = true;
|
|
|
|
const newExportDeclarationText = printer.printNode(
|
|
ts.EmitHint.Unspecified,
|
|
ts.createExportDeclaration(
|
|
undefined,
|
|
undefined,
|
|
ts.createNamedExports(exportSpecifiers),
|
|
moduleSpecifier,
|
|
),
|
|
source,
|
|
);
|
|
|
|
const recorder = tree.beginUpdate(mainFilePath);
|
|
if (updateExisting) {
|
|
const start = platformServerExports[0].getStart();
|
|
const width = platformServerExports[0].getWidth();
|
|
|
|
recorder
|
|
.remove(start, width)
|
|
.insertLeft(start, newExportDeclarationText);
|
|
} else {
|
|
recorder.insertLeft(source.getWidth(), '\n' + newExportDeclarationText);
|
|
}
|
|
|
|
tree.commitUpdate(recorder);
|
|
}
|
|
};
|
|
}
|