Skip to content
This repository was archived by the owner on Nov 22, 2024. It is now read-only.

Commit 50dd521

Browse files
Alan Agiusvikerman
authored andcommitted
feat(hapi-engine): update schematics to generate Ivy compatable code
With this change by default we support `ng add` only on Ivy applications. Users wishing to add this package in a non ivy project will need to this manually. **Note:**: for Ivy users are required to use `bundleDependencies` which is turned on by default in CLI version 9. Users opting out of `bundleDependencies` will need to run `ivy-ngcc` manually. The latter is rerquired because when not bundling the server using webpack during runtime Node will resolve the packages to their VE version.
1 parent 32cfef1 commit 50dd521

File tree

4 files changed

+48
-125
lines changed

4 files changed

+48
-125
lines changed

modules/hapi-engine/schematics/install/files/root/__serverFileName@stripTsExtension__.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,15 @@ const server = new Server({ port: PORT, host: 'localhost' });
2929
const DIST_FOLDER = join(process.cwd(), 'dist');
3030

3131
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
32-
const {AppServerModuleNgFactory, LAZY_MODULE_MAP, ngHapiEngine, provideModuleMap} = require('./<%= clientProject %>-server/main');
32+
const {AppServerModule, ngHapiEngine} = require('./<%= getServerDistDirectory() %>/main');
3333

3434
server.route({
3535
method: 'GET',
3636
path: '*',
3737
handler: (req: Request) =>
3838
ngHapiEngine({
39-
bootstrap: AppServerModuleNgFactory,
39+
bootstrap: AppServerModule,
4040
req,
41-
providers: [
42-
provideModuleMap(LAZY_MODULE_MAP)
43-
]
4441
})
4542
});
4643

modules/hapi-engine/schematics/install/files/root/webpack.server.config.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ module.exports = {
99
// This is our Hapi server for Dynamic universal
1010
server: './<%= stripTsExtension(serverFileName) %>.ts'
1111
},
12-
externals: {
13-
'./<%= getServerDistDirectory() %>/main': 'require("./server/main")'
14-
},
12+
externals: [
13+
'./<%= getServerDistDirectory() %>/main'
14+
],
1515
target: 'node',
1616
resolve: { extensions: ['.ts', '.js'] },
1717
optimization: {

modules/hapi-engine/schematics/install/index.spec.ts

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,6 @@ describe('Universal Schematic', () => {
2525
schematicRunner = new SchematicTestRunner('schematics', collectionPath);
2626
});
2727

28-
it('should add dependency: @nguniversal/module-map-ngfactory-loader',
29-
async () => {
30-
const tree = await schematicRunner
31-
.runSchematicAsync('ng-add', defaultOptions, appTree)
32-
.toPromise();
33-
const filePath = '/package.json';
34-
const contents = tree.readContent(filePath);
35-
expect(contents).toMatch(
36-
/\"@nguniversal\/module-map-ngfactory-loader\": \"/);
37-
});
38-
3928
it('should add dependency: @nguniversal/hapi-engine', async () => {
4029
const tree = await schematicRunner
4130
.runSchematicAsync('ng-add', defaultOptions, appTree)
@@ -116,23 +105,13 @@ describe('Universal Schematic', () => {
116105
expect(contents).toMatch('');
117106
});
118107

119-
it('should add module map loader to server module imports', async () => {
120-
const tree =
121-
await schematicRunner.runSchematicAsync('ng-add', defaultOptions, appTree)
122-
.toPromise();
123-
const filePath = '/projects/bar/src/app/app.server.module.ts';
124-
const contents = tree.readContent(filePath);
125-
expect(contents).toContain('ModuleMapLoaderModule');
126-
});
127-
128108
it('should add exports to main server file', async () => {
129109
const tree = await schematicRunner
130110
.runSchematicAsync('ng-add', defaultOptions, appTree)
131111
.toPromise();
132112
const filePath = '/projects/bar/src/main.server.ts';
133113
const contents = tree.readContent(filePath);
134114
expect(contents).toContain('ngHapiEngine');
135-
expect(contents).toContain('provideModuleMap');
136115
});
137116

138117
it('should update angular.json', async () => {
@@ -142,8 +121,8 @@ describe('Universal Schematic', () => {
142121
const contents = JSON.parse(tree.readContent('angular.json'));
143122
const architect = contents.projects.bar.architect;
144123
expect(architect.build.configurations.production).toBeDefined();
145-
expect(architect.build.options.outputPath).toBe('dist/browser');
146-
expect(architect.server.options.outputPath).toBe('dist/server');
124+
expect(architect.build.options.outputPath).toBe('dist/bar/browser');
125+
expect(architect.server.options.outputPath).toBe('dist/bar/server');
147126

148127
const productionConfig = architect.server.configurations.production;
149128
expect(productionConfig.fileReplacements).toBeDefined();

modules/hapi-engine/schematics/install/index.ts

Lines changed: 41 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8-
import {experimental, strings, normalize} from '@angular-devkit/core';
8+
import {strings, normalize, workspaces, join} from '@angular-devkit/core';
99
import {
1010
apply,
1111
chain,
@@ -21,7 +21,6 @@ import {
2121
url,
2222
} from '@angular-devkit/schematics';
2323
import {NodePackageInstallTask} from '@angular-devkit/schematics/tasks';
24-
import {getWorkspace} from '@schematics/angular/utility/config';
2524
import {Schema as UniversalOptions} from './schema';
2625
import {
2726
addPackageJsonDependency,
@@ -30,44 +29,29 @@ import {
3029
import {getProject} from '@schematics/angular/utility/project';
3130
import {getProjectTargets} from '@schematics/angular/utility/project-targets';
3231
import {InsertChange} from '@schematics/angular/utility/change';
33-
import {
34-
addSymbolToNgModuleMetadata,
35-
findNodes,
36-
insertAfterLastOccurrence,
37-
insertImport
38-
} from '@schematics/angular/utility/ast-utils';
32+
import {getWorkspace, updateWorkspace} from '@schematics/angular/utility/workspace';
33+
import {findNodes, insertAfterLastOccurrence} from '@schematics/angular/utility/ast-utils';
3934
import * as ts from 'typescript';
40-
import {findAppServerModulePath, generateExport, getTsSourceFile, getTsSourceText} from './utils';
41-
import {updateWorkspace} from '@schematics/angular/utility/workspace';
42-
43-
// TODO(CaerusKaru): make these configurable
44-
const BROWSER_DIST = 'dist/browser';
45-
const SERVER_DIST = 'dist/server';
46-
47-
function getClientProject(
48-
host: Tree, options: UniversalOptions,
49-
): experimental.workspace.WorkspaceProject {
50-
const workspace = getWorkspace(host);
51-
const clientProject = workspace.projects[options.clientProject];
52-
if (!clientProject) {
53-
throw new SchematicsException(`Client app ${options.clientProject} not found.`);
35+
import {generateExport, getTsSourceFile, getTsSourceText} from './utils';
36+
37+
async function getClientProject(host, projectName: string): Promise<workspaces.ProjectDefinition> {
38+
const workspace = await getWorkspace(host);
39+
const clientProject = workspace.projects.get(projectName);
40+
41+
if (!clientProject || clientProject.extensions.projectType !== 'application') {
42+
throw new SchematicsException(`Universal requires a project type of "application".`);
5443
}
5544

5645
return clientProject;
5746
}
5847

59-
function addDependenciesAndScripts(options: UniversalOptions): Rule {
48+
function addDependenciesAndScripts(options: UniversalOptions, serverDist: string): Rule {
6049
return (host: Tree) => {
6150
addPackageJsonDependency(host, {
6251
type: NodeDependencyType.Default,
6352
name: '@nguniversal/hapi-engine',
6453
version: '0.0.0-PLACEHOLDER',
6554
});
66-
addPackageJsonDependency(host, {
67-
type: NodeDependencyType.Default,
68-
name: '@nguniversal/module-map-ngfactory-loader',
69-
version: '0.0.0-PLACEHOLDER',
70-
});
7155
addPackageJsonDependency(host, {
7256
type: NodeDependencyType.Default,
7357
name: 'hapi',
@@ -100,23 +84,24 @@ function addDependenciesAndScripts(options: UniversalOptions): Rule {
10084
}
10185

10286
const pkg = JSON.parse(buffer.toString());
103-
104-
pkg.scripts['compile:server'] = options.webpack ?
105-
'webpack --config webpack.server.config.js --progress --colors' :
106-
`tsc -p ${serverFileName}.tsconfig.json`;
107-
pkg.scripts['serve:ssr'] = `node dist/${serverFileName}`;
108-
pkg.scripts['build:ssr'] = 'npm run build:client-and-server-bundles && npm run compile:server';
109-
pkg.scripts['build:client-and-server-bundles'] =
110-
// tslint:disable:max-line-length
111-
`ng build --prod && ng run ${options.clientProject}:server:production --bundleDependencies all`;
87+
pkg.scripts = {
88+
...pkg.scripts,
89+
'compile:server': options.webpack
90+
? 'webpack --config webpack.server.config.js --progress --colors'
91+
: `tsc -p ${serverFileName}.tsconfig.json`,
92+
'serve:ssr': `node ${serverDist.substr(1)}/${serverFileName}`,
93+
'build:ssr': 'npm run build:client-and-server-bundles && npm run compile:server',
94+
// tslint:disable-next-line: max-line-length
95+
'build:client-and-server-bundles': `ng build --prod && ng run ${options.clientProject}:server:production`,
96+
};
11297

11398
host.overwrite(pkgPath, JSON.stringify(pkg, null, 2));
11499

115100
return host;
116101
};
117102
}
118103

119-
function updateConfigFile(options: UniversalOptions) {
104+
function updateConfigFile(options: UniversalOptions, browserDist: string, serverDist: string) {
120105
return updateWorkspace((workspace => {
121106
const clientProject = workspace.projects.get(options.clientProject);
122107
if (clientProject) {
@@ -132,58 +117,17 @@ function updateConfigFile(options: UniversalOptions) {
132117

133118
serverTarget.options = {
134119
...serverTarget.options,
135-
outputPath: SERVER_DIST,
120+
outputPath: serverDist,
136121
};
137122

138123
buildTarget.options = {
139124
...buildTarget.options,
140-
outputPath: BROWSER_DIST,
125+
outputPath: browserDist,
141126
};
142127
}
143128
}));
144129
}
145130

146-
function addModuleMapLoader(options: UniversalOptions): Rule {
147-
return (host: Tree) => {
148-
const clientProject = getProject(host, options.clientProject);
149-
const clientTargets = getProjectTargets(clientProject);
150-
if (!clientTargets.server) {
151-
// If they skipped Universal schematics and don't have a server target,
152-
// just get out
153-
return;
154-
}
155-
const mainPath = normalize('/' + clientTargets.server.options.main);
156-
const appServerModuleRelativePath = findAppServerModulePath(host, mainPath);
157-
const modulePath = normalize(
158-
`/${clientProject.root}/src/${appServerModuleRelativePath}.ts`);
159-
160-
// Add the module map loader import
161-
let moduleSource = getTsSourceFile(host, modulePath);
162-
const importModule = 'ModuleMapLoaderModule';
163-
const importPath = '@nguniversal/module-map-ngfactory-loader';
164-
const moduleMapImportChange = insertImport(moduleSource, modulePath, importModule,
165-
importPath) as InsertChange;
166-
if (moduleMapImportChange) {
167-
const recorder = host.beginUpdate(modulePath);
168-
recorder.insertLeft(moduleMapImportChange.pos, moduleMapImportChange.toAdd);
169-
host.commitUpdate(recorder);
170-
}
171-
172-
// Add the module map loader module to the imports
173-
const importText = 'ModuleMapLoaderModule';
174-
moduleSource = getTsSourceFile(host, modulePath);
175-
const metadataChanges = addSymbolToNgModuleMetadata(
176-
moduleSource, modulePath, 'imports', importText);
177-
if (metadataChanges) {
178-
const recorder = host.beginUpdate(modulePath);
179-
metadataChanges.forEach((change: InsertChange) => {
180-
recorder.insertRight(change.pos, change.toAdd);
181-
});
182-
host.commitUpdate(recorder);
183-
}
184-
};
185-
}
186-
187131
function addExports(options: UniversalOptions): Rule {
188132
return (host: Tree) => {
189133
const clientProject = getProject(host, options.clientProject);
@@ -201,10 +145,8 @@ function addExports(options: UniversalOptions): Rule {
201145
const mainRecorder = host.beginUpdate(mainPath);
202146
const hapiEngineExport = generateExport(mainSourceFile, ['ngHapiEngine'],
203147
'@nguniversal/hapi-engine');
204-
const moduleMapExport = generateExport(mainSourceFile, ['provideModuleMap'],
205-
'@nguniversal/module-map-ngfactory-loader');
206148
const exports = findNodes(mainSourceFile, ts.SyntaxKind.ExportDeclaration);
207-
const addedExports = `\n${hapiEngineExport}\n${moduleMapExport}\n`;
149+
const addedExports = `\n${hapiEngineExport}\n`;
208150
const exportChange = insertAfterLastOccurrence(exports, addedExports, mainText,
209151
0) as InsertChange;
210152

@@ -214,11 +156,16 @@ function addExports(options: UniversalOptions): Rule {
214156
}
215157

216158
export default function (options: UniversalOptions): Rule {
217-
return (host: Tree, context: SchematicContext) => {
218-
const clientProject = getClientProject(host, options);
219-
if (clientProject.projectType !== 'application') {
220-
throw new SchematicsException(`Universal requires a project type of "application".`);
221-
}
159+
return async (host: Tree, context: SchematicContext) => {
160+
// Generate new output paths
161+
const clientProject = await getClientProject(host, options.clientProject);
162+
const {options: buildOptions} = clientProject.targets.get('build');
163+
const clientOutputPath = normalize(
164+
typeof buildOptions.outputPath === 'string' ? buildOptions.outputPath : 'dist'
165+
);
166+
167+
const browserDist = join(clientOutputPath, 'browser');
168+
const serverDist = join(clientOutputPath, 'server');
222169

223170
if (!options.skipInstall) {
224171
context.addTask(new NodePackageInstallTask());
@@ -232,18 +179,18 @@ export default function (options: UniversalOptions): Rule {
232179
...strings,
233180
...options as object,
234181
stripTsExtension: (s: string) => s.replace(/\.ts$/, ''),
235-
getBrowserDistDirectory: () => BROWSER_DIST,
236-
getServerDistDirectory: () => SERVER_DIST,
182+
// remove the leading slashes
183+
getBrowserDistDirectory: () => browserDist.substr(1),
184+
getServerDistDirectory: () => serverDist.substr(1),
237185
})
238186
]);
239187

240188
return chain([
241189
options.skipUniversal ?
242190
noop() : externalSchematic('@schematics/angular', 'universal', options),
243-
updateConfigFile(options),
191+
updateConfigFile(options, browserDist, serverDist),
244192
mergeWith(rootSource),
245-
addDependenciesAndScripts(options),
246-
addModuleMapLoader(options),
193+
addDependenciesAndScripts(options, serverDist),
247194
addExports(options),
248195
]);
249196
};

0 commit comments

Comments
 (0)