Skip to content

Commit 1f9f023

Browse files
mhartingtonimhoffd
andauthored
chore(builder): update builders to work with ng9 (#201)
* chore(builder): update builders to work with ng9 * chore(): removed unused dep * chore(): fix missing params * chore(): validate options first * chore(): add missing dev dep Co-authored-by: dwieeb <dwieeb@gmail.com>
1 parent e611b66 commit 1f9f023

File tree

5 files changed

+257
-44
lines changed

5 files changed

+257
-44
lines changed

builders/cordova-serve/index.ts

Lines changed: 104 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,121 @@
1-
import { BuilderContext, BuilderOutput, createBuilder, targetFromTargetString } from '@angular-devkit/architect';
1+
import {
2+
BuilderContext,
3+
createBuilder,
4+
targetFromTargetString
5+
} from '@angular-devkit/architect';
6+
import {
7+
ExecutionTransformer,
8+
executeDevServerBuilder
9+
} from '@angular-devkit/build-angular';
10+
import { ScriptsWebpackPlugin } from '@angular-devkit/build-angular/src/angular-cli-files/plugins/scripts-webpack-plugin';
11+
import { IndexHtmlTransform } from '@angular-devkit/build-angular/src/angular-cli-files/utilities/index-file/write-index-html';
12+
import {
13+
DevServerBuilderOptions,
14+
DevServerBuilderOutput
15+
} from '@angular-devkit/build-angular/src/dev-server';
216
import { json } from '@angular-devkit/core';
17+
import * as CopyWebpackPlugin from 'copy-webpack-plugin';
18+
import { basename } from 'path';
19+
import { Observable, from } from 'rxjs';
20+
import { switchMap } from 'rxjs/operators';
21+
import { Configuration } from 'webpack';
322

4-
import { prepareBrowserConfig } from '../utils';
23+
import { FormattedAssets, prepareServerConfig } from '../utils';
24+
import { augmentIndexHtml } from '../utils/append-scripts';
525

626
import { createConsoleLogServer } from './log-server';
727
import { CordovaServeBuilderSchema } from './schema';
828

9-
export type CordovaDevServerBuilderOptions = CordovaServeBuilderSchema & json.JsonObject;
29+
export type CordovaDevServerBuilderOptions = CordovaServeBuilderSchema &
30+
json.JsonObject;
1031

11-
export async function serveCordova(
32+
export function serveCordova(
1233
options: CordovaServeBuilderSchema,
1334
context: BuilderContext
14-
): Promise<BuilderOutput> {
15-
return new Promise(async () => {
16-
context.reportStatus(`running cordova serve...`);
17-
const { devServerTarget, cordovaBuildTarget, port, host, ssl } = options;
35+
): Observable<DevServerBuilderOutput> {
36+
const { devServerTarget, port, host, ssl } = options;
37+
const root = context.workspaceRoot;
38+
const devServerTargetSpec = targetFromTargetString(devServerTarget);
1839

19-
// Getting the original browser build options
20-
const cordovaBuildTargetSpec = targetFromTargetString(cordovaBuildTarget);
21-
const cordovaBuildTargetOptions = await context.getTargetOptions(cordovaBuildTargetSpec) as { browserTarget: string };
22-
const browserBuildTargetSpec = targetFromTargetString(cordovaBuildTargetOptions.browserTarget);
23-
24-
// What we actually need....
25-
const browserBuildTargetOptions = await context.getTargetOptions(browserBuildTargetSpec);
26-
27-
// Modifying those options to pass in cordova-speicfic stuff
28-
prepareBrowserConfig(options, browserBuildTargetOptions);
40+
async function setup() {
41+
const devServerTargetOptions = (await context.getTargetOptions(devServerTargetSpec)) as DevServerBuilderOptions;
42+
const devServerName = await context.getBuilderNameForTarget(devServerTargetSpec);
2943

44+
devServerTargetOptions.port = port;
45+
devServerTargetOptions.host = host;
46+
devServerTargetOptions.ssl = ssl;
47+
// tslint:disable-next-line: no-unnecessary-type-assertion
48+
const formattedOptions = await context.validateOptions(devServerTargetOptions, devServerName) as DevServerBuilderOptions;
49+
const formattedAssets = prepareServerConfig(options, root);
3050
if (options.consolelogs && options.consolelogsPort) {
3151
await createConsoleLogServer(host, options.consolelogsPort);
3252
}
53+
return { formattedOptions, formattedAssets };
54+
}
3355

34-
const devServerTargetSpec = targetFromTargetString(devServerTarget);
35-
const devServerTargetOptions = await context.getTargetOptions(devServerTargetSpec);
36-
37-
return context
38-
.scheduleTarget(devServerTargetSpec, { host, port, ssl }, devServerTargetOptions)
39-
.then(buildEvent => ({ ...buildEvent }));
40-
});
56+
return from(setup()).pipe(
57+
switchMap(({ formattedOptions, formattedAssets }) =>
58+
executeDevServerBuilder(
59+
formattedOptions,
60+
context,
61+
getTransforms(formattedAssets, context)
62+
)
63+
)
64+
);
4165
}
4266
export default createBuilder<CordovaDevServerBuilderOptions, any>(serveCordova);
67+
68+
function getTransforms(formattedAssets: FormattedAssets, context: BuilderContext) {
69+
return {
70+
webpackConfiguration: cordovaServeTransform(formattedAssets, context),
71+
indexHtml: indexHtmlTransformFactory(formattedAssets, context),
72+
};
73+
}
74+
75+
const cordovaServeTransform: (
76+
formattedAssets: FormattedAssets,
77+
context: BuilderContext
78+
) => ExecutionTransformer<Configuration> = (
79+
formattedAssets,
80+
{ workspaceRoot }
81+
) => browserWebpackConfig => {
82+
const scriptExtras = formattedAssets.globalScriptsByBundleName.map(
83+
(script: { bundleName: any; paths: any }) => {
84+
const bundleName = script.bundleName;
85+
return new ScriptsWebpackPlugin({
86+
name: bundleName,
87+
sourceMap: true,
88+
filename: `${basename(bundleName)}.js`,
89+
scripts: script.paths,
90+
basePath: workspaceRoot,
91+
});
92+
}
93+
);
94+
95+
const copyWebpackPluginOptions = {
96+
ignore: ['.gitkeep', '**/.DS_Store', '**/Thumbs.db'],
97+
};
98+
const copyWebpackPluginInstance = new CopyWebpackPlugin(
99+
formattedAssets.copyWebpackPluginPatterns,
100+
copyWebpackPluginOptions
101+
);
102+
// tslint:disable-next-line: no-non-null-assertion
103+
browserWebpackConfig.plugins!.push(
104+
...scriptExtras,
105+
copyWebpackPluginInstance
106+
);
107+
return browserWebpackConfig;
108+
};
109+
110+
export const indexHtmlTransformFactory: (
111+
formattedAssets: FormattedAssets,
112+
context: BuilderContext
113+
) => IndexHtmlTransform = ({ globalScriptsByBundleName }) => (
114+
indexTransform: string
115+
) => {
116+
const augmentedHtml = augmentIndexHtml(
117+
indexTransform,
118+
globalScriptsByBundleName
119+
);
120+
return Promise.resolve(augmentedHtml);
121+
};

builders/cordova-serve/log-server.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { terminal } from '@angular-devkit/core';
1+
import { red, reset, yellow } from 'colorette';
22
import * as util from 'util';
33
import * as WebSocket from 'ws';
44

@@ -31,7 +31,7 @@ export async function createConsoleLogServer(host: string, port: number): Promis
3131
data = data.toString();
3232
msg = JSON.parse(data);
3333
} catch (e) {
34-
process.stderr.write(`Error parsing JSON message from client: "${data}" ${terminal.red(e.stack ? e.stack : e)}\n`);
34+
process.stderr.write(`Error parsing JSON message from client: "${data}" ${red(e.stack ? e.stack : e)}\n`);
3535
return;
3636
}
3737

@@ -45,11 +45,11 @@ export async function createConsoleLogServer(host: string, port: number): Promis
4545
let status: ((_: string) => string) | undefined;
4646

4747
if (msg.type === 'info' || msg.type === 'log') {
48-
status = terminal.reset;
48+
status = reset;
4949
} else if (msg.type === 'error') {
50-
status = terminal.red;
50+
status = red;
5151
} else if (msg.type === 'warn') {
52-
status = terminal.yellow;
52+
status = yellow;
5353
}
5454

5555
// pretty print objects and arrays (no newlines for arrays)

builders/utils/append-scripts.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { load } from 'cheerio';
2+
3+
import { GlobalScriptsByBundleName } from '.';
4+
5+
export function augmentIndexHtml(indexString: string, scripts: GlobalScriptsByBundleName[]) {
6+
const $ = load(indexString);
7+
for (const script of scripts) {
8+
$('html').append(`<script src="${script.bundleName}.js"></script>`);
9+
}
10+
const final = $.html();
11+
return final;
12+
}

builders/utils/index.ts

Lines changed: 129 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
import { normalizeExtraEntryPoints } from '@angular-devkit/build-angular/src/angular-cli-files/models/webpack-configs/utils';
2+
import { AssetPatternClass } from '@angular-devkit/build-angular/src/browser/schema';
13
import { getSystemPath, join, normalize } from '@angular-devkit/core';
24
import { writeFileSync } from 'fs';
5+
import { resolve } from 'path';
36

47
import { CordovaBuildBuilderSchema } from '../cordova-build/schema';
58
import { CordovaServeBuilderSchema } from '../cordova-serve/schema';
@@ -68,21 +71,17 @@ export function prepareBrowserConfig(
6871
} }`
6972
);
7073
optionsStarter.scripts.push({
71-
input: configPath,
72-
bundleName: 'consolelogs',
73-
lazy: false,
74-
});
74+
input: configPath,
75+
bundleName: 'consolelogs',
76+
lazy: false,
77+
});
7578
optionsStarter.scripts.push({
76-
input: getSystemPath(
77-
join(
78-
normalize(__dirname),
79-
'../../assets',
80-
normalize('consolelogs.js')
81-
)
82-
),
83-
bundleName: 'consolelogs',
84-
lazy: false,
85-
});
79+
input: getSystemPath(
80+
join(normalize(__dirname), '../../assets', normalize('consolelogs.js'))
81+
),
82+
bundleName: 'consolelogs',
83+
lazy: false,
84+
});
8685
}
8786

8887
if (options.cordovaMock) {
@@ -125,3 +124,119 @@ export function prepareBrowserConfig(
125124

126125
return optionsStarter;
127126
}
127+
128+
export interface GlobalScriptsByBundleName {
129+
bundleName: string;
130+
paths: string[];
131+
inject: boolean;
132+
}
133+
export interface FormattedAssets {
134+
globalScriptsByBundleName: GlobalScriptsByBundleName[];
135+
136+
copyWebpackPluginPatterns: any[];
137+
}
138+
export function prepareServerConfig(
139+
options: CordovaServeBuilderSchema,
140+
root: string
141+
): FormattedAssets {
142+
const scripts = [];
143+
const assets = [];
144+
const cordovaBasePath = normalize(
145+
options.cordovaBasePath ? options.cordovaBasePath : '.'
146+
);
147+
if (options.consolelogs) {
148+
// Write the config to a file, and then include that in the bundle so it loads on window
149+
const configPath = getSystemPath(
150+
join(
151+
normalize(__dirname),
152+
'../../assets',
153+
normalize('consolelog-config.js')
154+
)
155+
);
156+
writeFileSync(
157+
configPath,
158+
`window.Ionic = window.Ionic || {}; Ionic.ConsoleLogServerConfig = { wsPort: ${
159+
options.consolelogsPort
160+
} }`
161+
);
162+
scripts.push({ input: configPath, bundleName: 'consolelogs', lazy: false });
163+
scripts.push({
164+
input: getSystemPath(
165+
join(normalize(__dirname), '../../assets', normalize('consolelogs.js'))
166+
),
167+
bundleName: 'consolelogs',
168+
lazy: false,
169+
});
170+
}
171+
if (options.cordovaMock) {
172+
scripts.push({
173+
input: getSystemPath(
174+
join(normalize(__dirname), '../../assets', normalize('cordova.js'))
175+
),
176+
bundleName: 'cordova',
177+
lazy: false,
178+
});
179+
} else if (options.cordovaAssets) {
180+
const platformWWWPath = join(
181+
cordovaBasePath,
182+
normalize(`platforms/${options.platform}/platform_www`)
183+
);
184+
assets.push({
185+
glob: '**/*',
186+
input: getSystemPath(platformWWWPath),
187+
output: './',
188+
});
189+
scripts.push({
190+
input: getSystemPath(join(platformWWWPath, normalize('cordova.js'))),
191+
bundleName: 'cordova',
192+
lazy: false,
193+
});
194+
}
195+
196+
const globalScriptsByBundleName = normalizeExtraEntryPoints(
197+
scripts,
198+
'scripts'
199+
).reduce(
200+
(
201+
prev: { bundleName: string; paths: string[]; inject: boolean }[],
202+
curr
203+
) => {
204+
const { bundleName, inject, input } = curr;
205+
const resolvedPath = resolve(root, input);
206+
const existingEntry = prev.find(el => el.bundleName === bundleName);
207+
if (existingEntry) {
208+
existingEntry.paths.push(resolvedPath);
209+
} else {
210+
prev.push({
211+
bundleName,
212+
inject,
213+
paths: [resolvedPath],
214+
});
215+
}
216+
return prev;
217+
},
218+
[]
219+
);
220+
221+
const copyWebpackPluginPatterns = assets.map((asset: AssetPatternClass) => {
222+
// Resolve input paths relative to workspace root and add slash at the end.
223+
asset.input = resolve(root, asset.input).replace(/\\/g, '/');
224+
asset.input = asset.input.endsWith('/') ? asset.input : asset.input + '/';
225+
asset.output = asset.output.endsWith('/')
226+
? asset.output
227+
: asset.output + '/';
228+
229+
return {
230+
context: asset.input,
231+
// Now we remove starting slash to make Webpack place it from the output root.
232+
to: asset.output.replace(/^\//, ''),
233+
ignore: asset.ignore,
234+
from: {
235+
glob: asset.glob,
236+
dot: true,
237+
},
238+
};
239+
});
240+
241+
return { globalScriptsByBundleName, copyWebpackPluginPatterns };
242+
}

package.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131
],
3232
"dependencies": {
3333
"@schematics/angular": "^8.0.0",
34+
"cheerio": "1.0.0-rc.3",
35+
"colorette": "1.1.0",
36+
"copy-webpack-plugin": "5.1.1",
3437
"tslib": "^1.9.0",
3538
"ws": "^7.0.1"
3639
},
@@ -43,13 +46,17 @@
4346
"@semantic-release/git": "^7.0.4",
4447
"@semantic-release/github": "^5.0.6",
4548
"@semantic-release/npm": "^5.0.4",
49+
"@types/cheerio": "0.22.15",
50+
"@types/copy-webpack-plugin": "5.0.0",
51+
"@types/karma": "3.0.5",
4652
"@types/node": "^8.10.34",
4753
"@types/webpack": "^4.4.14",
4854
"@types/webpack-dev-server": "^3.1.1",
4955
"@types/ws": "^7.2.1",
5056
"commitizen": "^4.0.3",
5157
"cz-conventional-changelog": "^3.1.0",
5258
"husky": "^4.0.10",
59+
"karma": "4.4.1",
5360
"lint-staged": "^10.0.4",
5461
"rimraf": "^3.0.1",
5562
"semantic-release": "^15.9.17",

0 commit comments

Comments
 (0)