Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 1 addition & 80 deletions packages/schematics/angular/universal/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
getPackageJsonDependency,
} from '../utility/dependencies';
import { latestVersions } from '../utility/latest-versions';
import { findBootstrapModuleCall, findBootstrapModulePath } from '../utility/ng-ast-utils';
import { findBootstrapModulePath } from '../utility/ng-ast-utils';
import { relativePathToWorkspaceRoot } from '../utility/paths';
import { targetBuildNotFoundError } from '../utility/project-targets';
import { getWorkspace, updateWorkspace } from '../utility/workspace';
Expand Down Expand Up @@ -109,84 +109,6 @@ function findBrowserModuleImport(host: Tree, modulePath: string): ts.Node {
return browserModuleNode;
}

function wrapBootstrapCall(mainFile: string): Rule {
return (host: Tree) => {
const mainPath = normalize('/' + mainFile);
let bootstrapCall: ts.Node | null = findBootstrapModuleCall(host, mainPath);
if (bootstrapCall === null) {
throw new SchematicsException('Bootstrap module not found.');
}

let bootstrapCallExpression: ts.Node | null = null;
let currentCall = bootstrapCall;
while (bootstrapCallExpression === null && currentCall.parent) {
currentCall = currentCall.parent;
if (ts.isExpressionStatement(currentCall) || ts.isVariableStatement(currentCall)) {
bootstrapCallExpression = currentCall;
}
}
bootstrapCall = currentCall;

// In case the bootstrap code is a variable statement
// we need to determine it's usage
if (bootstrapCallExpression && ts.isVariableStatement(bootstrapCallExpression)) {
const declaration = bootstrapCallExpression.declarationList.declarations[0];
const bootstrapVar = (declaration.name as ts.Identifier).text;
const sf = bootstrapCallExpression.getSourceFile();
bootstrapCall = findCallExpressionNode(sf, bootstrapVar) || currentCall;
}

// indent contents
const triviaWidth = bootstrapCall.getLeadingTriviaWidth();
const beforeText =
`function bootstrap() {\n` + ' '.repeat(triviaWidth > 2 ? triviaWidth + 1 : triviaWidth);
const afterText =
`\n${triviaWidth > 2 ? ' '.repeat(triviaWidth - 1) : ''}};\n` +
`

if (document.readyState === 'complete') {
bootstrap();
} else {
document.addEventListener('DOMContentLoaded', bootstrap);
}
`;

// in some cases we need to cater for a trailing semicolon such as;
// bootstrap().catch(err => console.log(err));
const lastToken = bootstrapCall.parent.getLastToken();
let endPos = bootstrapCall.getEnd();
if (lastToken && lastToken.kind === ts.SyntaxKind.SemicolonToken) {
endPos = lastToken.getEnd();
}

const recorder = host.beginUpdate(mainPath);
recorder.insertLeft(bootstrapCall.getStart(), beforeText);
recorder.insertRight(endPos, afterText);
host.commitUpdate(recorder);
};
}

function findCallExpressionNode(node: ts.Node, text: string): ts.Node | null {
if (
ts.isCallExpression(node) &&
ts.isIdentifier(node.expression) &&
node.expression.text === text
) {
return node;
}

let foundNode: ts.Node | null = null;
ts.forEachChild(node, (childNode) => {
foundNode = findCallExpressionNode(childNode, text);

if (foundNode) {
return true;
}
});

return foundNode;
}

function addServerTransition(
options: UniversalOptions,
mainFile: string,
Expand Down Expand Up @@ -292,7 +214,6 @@ export default function (options: UniversalOptions): Rule {
mergeWith(rootSource),
addDependencies(),
updateConfigFile(options, tsConfigDirectory),
wrapBootstrapCall(clientBuildOptions.main),
addServerTransition(options, clientBuildOptions.main, clientProject.root),
]);
};
Expand Down
30 changes: 0 additions & 30 deletions packages/schematics/angular/universal/index_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,36 +191,6 @@ describe('Universal Schematic', () => {
expect(contents).not.toContain(`withServerTransition({ appId: 'foo' })`);
});

it('should wrap the bootstrap call in a DOMContentLoaded event handler', async () => {
const tree = await schematicRunner.runSchematic('universal', defaultOptions, appTree);
const filePath = '/projects/bar/src/main.ts';
const contents = tree.readContent(filePath);
expect(contents).toContain(`document.addEventListener('DOMContentLoaded', bootstrap);`);
});

it('should wrap the bootstrap declaration in a DOMContentLoaded event handler', async () => {
const filePath = '/projects/bar/src/main.ts';
appTree.overwrite(
filePath,
`
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import { hmrBootstrap } from './hmr';

const bootstrap = () => platformBrowserDynamic().bootstrapModule(AppModule);

if (!hmrBootstrap) {
bootstrap().catch(err => console.log(err));
}
`,
);

const tree = await schematicRunner.runSchematic('universal', defaultOptions, appTree);
const contents = tree.readContent(filePath);
expect(contents).toContain(`document.addEventListener('DOMContentLoaded', bootstrap);`);
});

it('should install npm dependencies', async () => {
await schematicRunner.runSchematic('universal', defaultOptions, appTree);
expect(schematicRunner.tasks.length).toBe(1);
Expand Down