Skip to content

Commit 772f2a8

Browse files
committed
feat(ng-update): implement ng update migration
1 parent a50877b commit 772f2a8

File tree

14 files changed

+298
-17
lines changed

14 files changed

+298
-17
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
# 8.0.0-beta.1 (2019-07-14)
2+
3+
### Features
4+
5+
* Added additionalEnvironmentVariables to builder options
6+
* Implemented ng update migration
7+
18
# 8.0.0-beta.0 (2019-06-30)
29

310
### Features

README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# angular-server-side-configuration
22

3-
![](https://img.shields.io/azure-devops/build/kyubisation/894749fe-3edd-41f8-818f-ba14e6a3cc22/1/master.svg)
4-
![](https://img.shields.io/azure-devops/coverage/kyubisation/angular-server-side-configuration/1/master.svg)
3+
![](https://img.shields.io/azure-devops/build/kyubisation/894749fe-3edd-41f8-818f-ba14e6a3cc22/2/master.svg)
4+
![](https://img.shields.io/azure-devops/coverage/kyubisation/angular-server-side-configuration/2/master.svg)
55
![](https://img.shields.io/npm/v/angular-server-side-configuration.svg)
66
![](https://img.shields.io/npm/l/angular-server-side-configuration.svg)
77

@@ -30,6 +30,10 @@ of this library can be used, as it is Angular version agnostic.
3030
```
3131
ng add angular-server-side-configuration
3232
```
33+
or, if you have a previous version of this library installed
34+
```
35+
ng update angular-server-side-configuration@latest
36+
```
3337
This will configure the appropriate files.
3438

3539
Alternatively, if you want to configure the files yourself:
@@ -58,6 +62,7 @@ used environment variables and generate an [ngssc.json](#ngsscjson) in the defin
5862
"ngsscbuild": {
5963
"builder": "angular-server-side-configuration:ngsscbuild",
6064
"options": {
65+
"additionalEnvironmentVariables": ["MANUAL_ENTRIES"],
6166
"aotSupport": true, // Set this to true, if you need to use
6267
// environment variables inside AoT contexts
6368
// (e.g. forRoot(...) or forChild(...))
@@ -79,6 +84,8 @@ used environment variables and generate an [ngssc.json](#ngsscjson) in the defin
7984
...
8085
```
8186

87+
To run the ngssc build, run the command `ng run your-project-name:ngsscbuild:production`.
88+
8289
### environment.prod.ts
8390
angular-server-side-configuration supports two variants for using environment variables:
8491
process.env.* or NG_ENV.*

builders/ngsscbuild/index.spec.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ describe('Ngssc Builder', () => {
6969
addDummyBuildTarget({});
7070
try {
7171
await runNgsscbuild({
72+
additionalEnvironmentVariables: [],
7273
aotSupport: true,
7374
browserTarget: 'dummy:build',
7475
ngsscEnvironmentFile: 'environment.prod.ts',
@@ -82,6 +83,7 @@ describe('Ngssc Builder', () => {
8283
it('should build with process variant', async () => {
8384
addDummyBuildTarget();
8485
const output = await runNgsscbuild({
86+
additionalEnvironmentVariables: [],
8587
aotSupport: false,
8688
browserTarget: 'dummy:build',
8789
ngsscEnvironmentFile: 'environment.prod.ts',
@@ -94,9 +96,26 @@ describe('Ngssc Builder', () => {
9496
expect(ngssc.filePattern).toEqual('index.html');
9597
});
9698

99+
it('should aggregate environment variables', async () => {
100+
const expected = 'OTHER_VARIABLE';
101+
addDummyBuildTarget();
102+
const output = await runNgsscbuild({
103+
additionalEnvironmentVariables: [expected],
104+
aotSupport: false,
105+
browserTarget: 'dummy:build',
106+
ngsscEnvironmentFile: 'environment.prod.ts',
107+
});
108+
109+
expect(output.success).toBe(true);
110+
expect(logger.includes('ngssc')).toBeTruthy();
111+
const ngssc: Ngssc = JSON.parse(readFileSync(join(distDir, 'ngssc.json'), 'utf8'));
112+
expect(ngssc.environmentVariables).toContain(expected);
113+
});
114+
97115
it('should build with aotSupport and process variant', async () => {
98116
addDummyBuildTarget();
99117
const output = await runNgsscbuild({
118+
additionalEnvironmentVariables: [],
100119
aotSupport: true,
101120
browserTarget: 'dummy:build',
102121
ngsscEnvironmentFile: 'environment.prod.ts',

builders/ngsscbuild/models/options.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { JsonObject } from '@angular-devkit/core';
22

33
export interface Options extends JsonObject {
4+
additionalEnvironmentVariables: string[];
45
aotSupport: boolean;
56
browserTarget: string;
67
ngsscEnvironmentFile: string;

builders/ngsscbuild/ngssc-builder.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export class NgsscBuilder {
4848
private async _safeRun() {
4949
const ngsscContext = await this._detectVariables();
5050
const rawOptions = await this._prepareBrowserOptions(ngsscContext);
51-
const browserTarget = targetFromTargetString(this._options.browserTarget);
51+
const browserTarget = this._browserTarget;
5252
const browserName = await this._context.getBuilderNameForTarget(browserTarget);
5353
const browserOptions = await this._context.validateOptions<BrowserOptions>(rawOptions, browserName);
5454
const scheduledTarget = await this._context.scheduleTarget(browserTarget, browserOptions);
@@ -100,7 +100,10 @@ export class NgsscBuilder {
100100
private async _buildNgsscJson(ngsscContext: NgsscContext, browserOptions: BrowserOptions) {
101101
const outputPath = join(this._context.workspaceRoot, browserOptions.outputPath);
102102
const ngssc: Ngssc = {
103-
environmentVariables: ngsscContext.variables.map(m => m.variable),
103+
environmentVariables: [
104+
...ngsscContext.variables.map(m => m.variable),
105+
...(this._options.additionalEnvironmentVariables || []),
106+
],
104107
filePattern: basename(browserOptions.index),
105108
variant: ngsscContext.variant,
106109
};

builders/ngsscbuild/schema.json

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,21 @@
22
"$schema": "http://json-schema.org/schema",
33
"type": "object",
44
"properties": {
5-
"command": {
6-
"type": "string"
7-
},
8-
"args": {
5+
"additionalEnvironmentVariables": {
96
"type": "array",
10-
"items": {
11-
"type": "string"
12-
}
7+
"description": "Additional environment variables that should be added to ngssc.json"
8+
},
9+
"aotSupport": {
10+
"type": "boolean",
11+
"description": "Whether variables are going to be used in AoT contexts (like forRoot(...) or forChild(...))"
12+
},
13+
"browserTarget": {
14+
"type": "string",
15+
"description": "The build target to execute"
16+
},
17+
"ngsscEnvironmentFile": {
18+
"type": "string",
19+
"description": "The ngssc environment file (usually src/environments/environment.prod.ts)"
1320
}
1421
}
1522
}

package.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
},
88
"builders": "./builders/builders.json",
99
"schematics": "./schematics/collection.json",
10+
"ng-update": {
11+
"migrations": "./schematics/migration.json"
12+
},
1013
"scripts": {
1114
"clean": "rimraf coverage dist {builders,models,schematics}/**/*.{d.ts,js} test/*.{d.ts,js} junit.xml",
1215
"build:node": "npm run clean && tsc",
@@ -80,10 +83,10 @@
8083
"coverageDirectory": "coverage",
8184
"coverageThreshold": {
8285
"global": {
83-
"branches": 90,
84-
"functions": 90,
85-
"lines": 90,
86-
"statements": 90
86+
"branches": 85,
87+
"functions": 95,
88+
"lines": 95,
89+
"statements": 95
8790
}
8891
}
8992
}

schematics/migration.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json",
3+
"schematics": {
4+
"migration-v8": {
5+
"version": "8-beta",
6+
"description": "Updates angular-server-side-configuration to v8",
7+
"factory": "./ng-update/index#updateToV8"
8+
}
9+
}
10+
}

schematics/ng-add/index.spec.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { Tree } from '@angular-devkit/schematics';
21
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
32
import { Schema as ApplicationOptions, Style } from '@schematics/angular/application/schema';
43
import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema';
@@ -91,6 +90,17 @@ describe('ng-add', () => {
9190
assertAppliedConfig(tree);
9291
});
9392

93+
it('should add ngssc content to correct files and split additional environment variables', async () => {
94+
const expected = 'OTHER_VARIABLES,OTHER_VARIABLES2';
95+
const tree = await runner
96+
.runSchematicAsync('ng-add', { additionalEnvironmentVariables: expected }, appTree)
97+
.toPromise();
98+
assertAppliedConfig(tree);
99+
const angularJson = JSON.parse(tree.readContent('angular.json'));
100+
expect(angularJson.projects.dummy.architect.ngsscbuild.options.additionalEnvironmentVariables)
101+
.toEqual(expected.split(','));
102+
});
103+
94104
it('should add ngssc content to correct files, with missing title tag', async () => {
95105
const htmlContent = appTree.readContent(htmlPath);
96106
appTree.overwrite(htmlPath, htmlContent.replace(/<title>[^<]+<\/title>/, ''));

schematics/ng-add/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ function addNgsscTargetToWorkspace(options: Schema) {
2727
architect.ngsscbuild = {
2828
builder: 'angular-server-side-configuration:ngsscbuild',
2929
options: {
30+
additionalEnvironmentVariables: options.additionalEnvironmentVariables
31+
? options.additionalEnvironmentVariables.split(',').map(e => e.trim()) : [],
3032
aotSupport: options.aotSupport,
3133
browserTarget: `${projectName}:build`,
3234
ngsscEnvironmentFile: options.ngsscEnvironmentFile,
@@ -94,7 +96,7 @@ function addNgsscToPackageScripts(options: Schema) {
9496
throw new SchematicsException('Could not find package.json');
9597
}
9698

97-
const pkg = JSON.parse(buffer.toString());
99+
const pkg = { scripts: {}, ...JSON.parse(buffer.toString()) };
98100
if ('build:ngssc' in pkg.scripts) {
99101
context.logger.info(`Skipping adding script to package.json, as it already exists.`);
100102
return;

schematics/ng-add/schema.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@
4141
"type": "boolean",
4242
"default": true,
4343
"x-prompt": "Are variables going to be used in AoT contexts (like forRoot(...) or forChild(...)):"
44+
},
45+
"additionalEnvironmentVariables": {
46+
"description": "Additional environment variables that should be added to ngssc.json",
47+
"type": "string",
48+
"x-prompt": "Add additional environment variables (comma-separated):"
4449
}
4550
}
4651
}

schematics/ng-add/schema.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export interface Schema {
2+
additionalEnvironmentVariables: string;
23
/** Name of the project. */
34
project: string;
45
variant: 'process' | 'NG_ENV';

schematics/ng-update/index.spec.ts

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
2+
import { Schema as ApplicationOptions, Style } from '@schematics/angular/application/schema';
3+
import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema';
4+
import { join } from 'path';
5+
6+
const workspaceOptions: WorkspaceOptions = {
7+
name: 'workspace',
8+
newProjectRoot: 'projects',
9+
version: '6.0.0',
10+
};
11+
12+
const appOptions: ApplicationOptions = {
13+
inlineStyle: false,
14+
inlineTemplate: false,
15+
name: 'dummy',
16+
routing: false,
17+
skipPackageJson: false,
18+
skipTests: false,
19+
style: Style.Css,
20+
};
21+
22+
describe('ng-update', () => {
23+
const collectionPath = join(__dirname, '../migration.json');
24+
const envPath = 'projects/dummy/src/environments/environment.prod.ts';
25+
const htmlPath = 'projects/dummy/src/index.html';
26+
let runner: SchematicTestRunner;
27+
let appTree: UnitTestTree;
28+
29+
beforeEach(async () => {
30+
runner = new SchematicTestRunner('schematics', collectionPath);
31+
appTree = await runner
32+
.runExternalSchematicAsync('@schematics/angular', 'workspace', workspaceOptions)
33+
.toPromise();
34+
appTree = await runner
35+
.runExternalSchematicAsync('@schematics/angular', 'application', appOptions, appTree)
36+
.toPromise();
37+
});
38+
39+
function assertAppliedConfig(
40+
tree: UnitTestTree, importString = `import 'angular-server-side-configuration/process';`) {
41+
const angularJson = JSON.parse(tree.readContent('angular.json'));
42+
expect(angularJson.projects.dummy.architect.ngsscbuild.builder)
43+
.toBe('angular-server-side-configuration:ngsscbuild');
44+
45+
const environmentContent = tree.readContent(envPath);
46+
expect(environmentContent).toContain(importString);
47+
48+
const indexContent = tree.readContent(htmlPath);
49+
expect(indexContent).toContain('<!--CONFIG-->');
50+
51+
const packageJson = JSON.parse(tree.readContent('package.json'));
52+
expect(packageJson.scripts['build:ngssc']).toContain(':ngsscbuild:production');
53+
}
54+
55+
it('should add ngssc content to correct files', async () => {
56+
const tree = await runner
57+
.runSchematicAsync('migration-v8', {}, appTree)
58+
.toPromise();
59+
assertAppliedConfig(tree);
60+
});
61+
62+
it('should migrate ng-env4 import', async () => {
63+
const envContent = appTree.read(envPath)!.toString('utf8');
64+
appTree.overwrite(
65+
envPath,
66+
envContent.replace(
67+
/^/, `import { NG_ENV } from 'angular-server-side-configuration/ng-env4';\n`));
68+
const tree = await runner
69+
.runSchematicAsync('migration-v8', {}, appTree)
70+
.toPromise();
71+
assertAppliedConfig(tree, `import { NG_ENV } from 'angular-server-side-configuration/ng-env';`);
72+
});
73+
74+
it('should detect ng-env variant', async () => {
75+
const envContent = appTree.read(envPath)!.toString('utf8');
76+
appTree.overwrite(
77+
envPath,
78+
envContent.replace(
79+
/^/, `import { NG_ENV } from 'angular-server-side-configuration/ng-env';\n`));
80+
const tree = await runner
81+
.runSchematicAsync('migration-v8', {}, appTree)
82+
.toPromise();
83+
assertAppliedConfig(tree, `import { NG_ENV } from 'angular-server-side-configuration/ng-env';`);
84+
});
85+
86+
it('should detect and remove ngssc.json', async () => {
87+
const expected = 'OTHER_VARIABLES';
88+
appTree.create('/ngssc.json', JSON.stringify({ environmentVariables: [expected] }));
89+
const tree = await runner
90+
.runSchematicAsync('migration-v8', {}, appTree)
91+
.toPromise();
92+
assertAppliedConfig(tree);
93+
const angularJson = JSON.parse(tree.readContent('angular.json'));
94+
expect(angularJson.projects.dummy.architect.ngsscbuild.options.additionalEnvironmentVariables)
95+
.toEqual([expected]);
96+
expect(appTree.exists('/ngssc.json')).toBeFalsy();
97+
});
98+
99+
it('should detect ngssc usage in package.json', async () => {
100+
const packageJsonPath = '/package.json';
101+
const pkg = JSON.parse(appTree.read(packageJsonPath)!.toString('utf8'));
102+
pkg.scripts = { ...pkg.scripts, 'build:ngssc': 'ngssc ng build' };
103+
appTree.overwrite(packageJsonPath, JSON.stringify(pkg));
104+
const logs: string[] = [];
105+
runner.logger.subscribe(m => logs.push(m.message));
106+
await runner
107+
.runSchematicAsync('migration-v8', {}, appTree)
108+
.toPromise();
109+
expect(logs.some(l => l.includes('Please remove the ngssc usage from your scripts.')))
110+
.toBeTruthy();
111+
});
112+
113+
it('should not fail when no scripts are defined in package.json', async () => {
114+
const packageJsonPath = '/package.json';
115+
const pkg = JSON.parse(appTree.read(packageJsonPath)!.toString('utf8'));
116+
delete pkg.scripts;
117+
appTree.overwrite(packageJsonPath, JSON.stringify(pkg));
118+
await runner
119+
.runSchematicAsync('migration-v8', {}, appTree)
120+
.toPromise();
121+
});
122+
});

0 commit comments

Comments
 (0)