Skip to content

Commit a6cd9af

Browse files
feat: update Angular template to version 17 (#4280)
* build: update local PNPM version * feat: update Angular template to version 17 This commits updates the Angular template to use Angular 17. * ci: only run c3 e2e tests on the cloudflare fork There is no point in running on other people's forks since they don't have access to the API token etc. --------- Co-authored-by: Peter Bacon Darwin <pete@bacondarwin.com>
1 parent a1c9f43 commit a6cd9af

13 files changed

Lines changed: 95 additions & 169 deletions

File tree

.changeset/spotty-bees-arrive.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"create-cloudflare": minor
3+
---
4+
5+
Update Angular template to use version 17

.github/workflows/c3-e2e.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ env:
1111
jobs:
1212
e2e:
1313
name: ${{ format('E2E ({0})', matrix.pm) }}
14-
if: |
15-
github.event.pull_request.user.login != 'dependabot[bot]'
14+
if: github.repository_owner == 'cloudflare' && github.event.pull_request.user.login != 'dependabot[bot]'
1615
strategy:
1716
matrix:
1817
os: [ubuntu-latest]

packages/create-cloudflare/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
"glob": "^10.3.3",
6868
"haikunator": "^2.1.2",
6969
"open": "^8.4.0",
70-
"pnpm": "^8.6.11",
70+
"pnpm": "^8.10.0",
7171
"recast": "^0.22.0",
7272
"semver": "^7.5.1",
7373
"shell-quote": "^1.8.1",

packages/create-cloudflare/src/frameworks/angular/index.ts

Lines changed: 23 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,44 @@
1-
import { cp, rm } from "node:fs/promises";
1+
import { cp } from "node:fs/promises";
22
import { resolve } from "node:path";
33
import { logRaw } from "@cloudflare/cli";
44
import { brandColor, dim } from "@cloudflare/cli/colors";
55
import { spinner } from "@cloudflare/cli/interactive";
6-
import {
7-
installPackages,
8-
runCommand,
9-
runFrameworkGenerator,
10-
} from "helpers/command";
6+
import { installPackages, runFrameworkGenerator } from "helpers/command";
117
import { compatDateFlag, readFile, readJSON, writeFile } from "helpers/files";
128
import { detectPackageManager } from "helpers/packages";
139
import { getFrameworkCli } from "../index";
1410
import type { FrameworkConfig, PagesGeneratorContext } from "types";
1511

16-
const { dlx, npx, npm } = detectPackageManager();
12+
const { dlx, npm } = detectPackageManager();
1713

1814
const generate = async (ctx: PagesGeneratorContext) => {
1915
const cli = getFrameworkCli(ctx);
2016

21-
await runFrameworkGenerator(
22-
ctx,
23-
`${dlx} ${cli} new ${ctx.project.name} --standalone`
24-
);
17+
await runFrameworkGenerator(ctx, `${dlx} ${cli} ${ctx.project.name} --ssr`);
2518

2619
logRaw("");
2720
};
2821

2922
const configure = async (ctx: PagesGeneratorContext) => {
3023
process.chdir(ctx.project.path);
31-
await runCommand(`${npx} ng analytics disable`, {
32-
silent: true,
33-
});
34-
await addSSRAdapter();
35-
await installCFWorker(ctx);
36-
await updateAppCode();
3724
updateAngularJson(ctx);
25+
await updateAppCode();
26+
await installCFWorker(ctx);
3827
};
3928

4029
const config: FrameworkConfig = {
4130
generate,
4231
configure,
4332
displayName: "Angular",
4433
getPackageScripts: async () => ({
45-
process:
46-
"node ./tools/copy-worker-files.mjs && node ./tools/copy-client-files.mjs && node ./tools/bundle.mjs",
47-
"pages:build": `${npm} run build:ssr && ${npm} run process`,
4834
start: `${npm} run pages:build && wrangler pages dev dist/cloudflare ${await compatDateFlag()} --experimental-local`,
35+
process: "node ./tools/copy-files.mjs && node ./tools/alter-polyfills.mjs",
36+
"pages:build": `ng build && ${npm} run process`,
4937
deploy: `${npm} run pages:build && wrangler pages deploy dist/cloudflare`,
5038
}),
5139
deployCommand: "deploy",
5240
devCommand: "start",
53-
testFlags: ["--routing", "--style", "sass"],
41+
testFlags: ["--ssr", "--style", "sass"],
5442
};
5543
export default config;
5644

@@ -66,67 +54,51 @@ async function installCFWorker(ctx: PagesGeneratorContext) {
6654
s.stop(`${brandColor("copied")} ${dim("adapter code")}`);
6755

6856
await installPackages(
69-
[
70-
"@cloudflare/workers-types",
71-
"@esbuild-plugins/node-globals-polyfill",
72-
"@esbuild-plugins/node-modules-polyfill",
73-
"@miniflare/tre@next",
74-
"esbuild",
75-
"fast-glob",
76-
"wrangler@beta",
77-
],
57+
["@cloudflare/workers-types", "@miniflare/tre@next", "wrangler@beta"],
7858
{
7959
dev: true,
8060
startText: "Installing adapter dependencies",
8161
doneText: `${brandColor("installed")} ${dim(`via \`${npm} install\``)}`,
8262
}
8363
);
8464
}
85-
86-
async function addSSRAdapter() {
87-
const cmd = `${npx} ng add @nguniversal/express-engine`;
88-
89-
await runCommand(`${cmd} --skip-confirmation`, {
90-
silent: true,
91-
startText: "Installing Angular SSR",
92-
doneText: `${brandColor("installed")} ${dim(`via \`${cmd}\``)}`,
93-
});
94-
}
95-
9665
async function updateAppCode() {
9766
const s = spinner();
9867
s.start(`Updating application code`);
9968

10069
// Update an app config file to:
101-
// - add the `provideClientHydration()` provider to enable hydration
10270
// - add the `provideHttpClient(withFetch())` call to enable `fetch` usage in `HttpClient`
10371
const appConfigPath = "src/app/app.config.ts";
10472
const appConfig = readFile(resolve(appConfigPath));
10573
const newAppConfig =
106-
"import { provideClientHydration } from '@angular/platform-browser';\n" +
10774
"import { provideHttpClient, withFetch } from '@angular/common/http';\n" +
10875
appConfig.replace(
10976
"providers: [",
110-
"providers: [provideHttpClient(withFetch()), provideClientHydration(), "
77+
"providers: [provideHttpClient(withFetch()), "
11178
);
11279
writeFile(resolve(appConfigPath), newAppConfig);
80+
s.stop(`${brandColor(`updated`)} ${dim(appConfigPath)}`);
11381

114-
// Remove the unwanted node.js server entry-point
115-
await rm(resolve("server.ts"));
82+
// Remove unwanted dependencies
83+
s.start(`Updating package.json`);
84+
const packageJsonPath = resolve("package.json");
85+
const packageManifest = readJSON(packageJsonPath);
11686

117-
s.stop(`${brandColor(`updated`)} ${dim(appConfigPath)}`);
87+
delete packageManifest["dependencies"]["@angular/ssr"];
88+
delete packageManifest["dependencies"]["express"];
89+
90+
writeFile(packageJsonPath, JSON.stringify(packageManifest, null, 2));
91+
s.stop(`${brandColor(`updated`)} ${dim(`\`package.json\``)}`);
11892
}
11993

12094
function updateAngularJson(ctx: PagesGeneratorContext) {
12195
const s = spinner();
12296
s.start(`Updating angular.json config`);
12397
const angularJson = readJSON(resolve("angular.json"));
98+
// Update builder
12499
const architectSection = angularJson.projects[ctx.project.name].architect;
125-
architectSection.build.options.outputPath = "dist/browser";
100+
architectSection.build.options.outputPath = "dist";
126101
architectSection.build.options.assets.push("src/_routes.json");
127-
architectSection.server.options.outputPath = "dist/server";
128-
architectSection.server.options.main = "src/main.server.ts";
129-
delete architectSection["serve-ssr"];
130102

131103
writeFile(resolve("angular.json"), JSON.stringify(angularJson, null, 2));
132104
s.stop(`${brandColor(`updated`)} ${dim(`\`angular.json\``)}`);

packages/create-cloudflare/src/frameworks/angular/templates/src/main.server.ts renamed to packages/create-cloudflare/src/frameworks/angular/templates/server.ts

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
1-
import "zone.js/dist/zone-node";
2-
import "@angular/platform-server/init";
3-
4-
import { bootstrapApplication } from "@angular/platform-browser";
51
import { renderApplication } from "@angular/platform-server";
6-
7-
import { AppComponent } from "./app/app.component";
8-
import { config } from "./app/app.config.server";
2+
import bootstrap from "./src/main.server";
93

104
interface Env {
115
ASSETS: { fetch: typeof fetch };
@@ -14,10 +8,7 @@ interface Env {
148
// We attach the Cloudflare `fetch()` handler to the global scope
159
// so that we can export it when we process the Angular output.
1610
// See tools/bundle.mjs
17-
(globalThis as any).__workerFetchHandler = async function fetch(
18-
request: Request,
19-
env: Env
20-
) {
11+
async function workerFetchHandler(request: Request, env: Env) {
2112
const url = new URL(request.url);
2213
console.log("render SSR", url.href);
2314

@@ -26,10 +17,18 @@ interface Env {
2617
const indexResponse = await env.ASSETS.fetch(new Request(indexUrl));
2718
const document = await indexResponse.text();
2819

29-
const content = await renderApplication(
30-
() => bootstrapApplication(AppComponent, config),
31-
{ document, url: url.pathname }
32-
);
20+
const content = await renderApplication(bootstrap, {
21+
document,
22+
url: url.pathname,
23+
});
24+
3325
// console.log("rendered SSR", content);
3426
return new Response(content, indexResponse);
27+
}
28+
29+
export default {
30+
fetch: (request: Request, env: Env) =>
31+
(globalThis as any)["__zone_symbol__Promise"].resolve(
32+
workerFetchHandler(request, env)
33+
),
3534
};
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { EOL } from "node:os";
2+
import fs from "node:fs";
3+
import { join } from "node:path";
4+
import { worker } from "./paths.mjs";
5+
6+
/**
7+
* Split by lines and comment the banner
8+
* ```
9+
* import { createRequire } from 'node:module';
10+
* globalThis['require'] ??= createRequire(import.meta.url);
11+
* ```
12+
*/
13+
const serverPolyfillsFile = join(worker, "polyfills.server.mjs");
14+
const serverPolyfillsData = fs
15+
.readFileSync(serverPolyfillsFile, "utf8")
16+
.split(EOL);
17+
18+
for (let index = 0; index < 2; index++) {
19+
if (serverPolyfillsData[index].includes("createRequire")) {
20+
serverPolyfillsData[index] = "// " + serverPolyfillsData[index];
21+
}
22+
}
23+
24+
// Add needed polyfills
25+
serverPolyfillsData.unshift(
26+
`globalThis['process'] = {};`,
27+
`globalThis['global'] = globalThis;`,
28+
// Needed as performance.mark is not a function in worker.
29+
`performance.mark = () => {};`
30+
);
31+
32+
fs.writeFileSync(serverPolyfillsFile, serverPolyfillsData.join(EOL));

packages/create-cloudflare/src/frameworks/angular/templates/tools/bundle.mjs

Lines changed: 0 additions & 77 deletions
This file was deleted.

packages/create-cloudflare/src/frameworks/angular/templates/tools/copy-client-files.mjs

Lines changed: 0 additions & 4 deletions
This file was deleted.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Copy the files over so that they can be uploaded by the pages publish command.
2+
import fs from "node:fs";
3+
import { join } from "node:path";
4+
import { client, cloudflare, worker, ssr } from "./paths.mjs";
5+
6+
fs.cpSync(client, cloudflare, { recursive: true });
7+
fs.cpSync(ssr, worker, { recursive: true });
8+
9+
fs.renameSync(join(worker, "server.mjs"), join(worker, "index.js"));

packages/create-cloudflare/src/frameworks/angular/templates/tools/copy-worker-files.mjs

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)