update-server-main-file.js 4.71 KB
Newer Older
Kriengkrai Yothee's avatar
Kriengkrai Yothee committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const ts = require("../../third_party/github.com/Microsoft/TypeScript/lib/typescript");
const ast_utils_1 = require("../../utility/ast-utils");
const json_utils_1 = require("../../utility/json-utils");
const workspace_models_1 = require("../../utility/workspace-models");
const utils_1 = require("./utils");
/**
 * 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`.
 */
function updateServerMainFile() {
    return tree => {
        const workspace = utils_1.getWorkspace(tree);
        for (const { target } of utils_1.getTargets(workspace, 'server', workspace_models_1.Builders.Server)) {
            const options = json_utils_1.findPropertyInAstObject(target, 'options');
            if (!options || options.kind !== 'object') {
                continue;
            }
            // find the main server file
            const mainFile = json_utils_1.findPropertyInAstObject(options, 'main');
            if (!mainFile || typeof mainFile.value !== 'string') {
                continue;
            }
            const mainFilePath = mainFile.value;
            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 = ast_utils_1.findNodes(source, ts.SyntaxKind.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 = [];
            let updateExisting = false;
            // Add missing exports
            if (platformServerExports.length) {
                const { exportClause } = platformServerExports[0];
                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.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);
        }
        return tree;
    };
}
exports.updateServerMainFile = updateServerMainFile;