diff --git a/.changeset/cool-experts-punch.md b/.changeset/cool-experts-punch.md new file mode 100644 index 000000000000..93a65192ca88 --- /dev/null +++ b/.changeset/cool-experts-punch.md @@ -0,0 +1,5 @@ +--- +"create-cloudflare": minor +--- + +Update Angular template to version 19 diff --git a/packages/create-cloudflare/src/frameworks/package.json b/packages/create-cloudflare/src/frameworks/package.json index 69297af822f8..d7dc915b89f5 100644 --- a/packages/create-cloudflare/src/frameworks/package.json +++ b/packages/create-cloudflare/src/frameworks/package.json @@ -7,7 +7,7 @@ "dependencies": { "create-astro": "4.10.0", "create-analog": "1.8.1", - "@angular/create": "18.2.11", + "@angular/create": "19.0.0", "create-docusaurus": "3.6.1", "create-hono": "0.14.2", "create-next-app": "14.2.5", diff --git a/packages/create-cloudflare/templates/angular/c3.ts b/packages/create-cloudflare/templates/angular/c3.ts index 4522b4441374..b910642a2e98 100644 --- a/packages/create-cloudflare/templates/angular/c3.ts +++ b/packages/create-cloudflare/templates/angular/c3.ts @@ -13,7 +13,11 @@ import type { C3Context } from "types"; const { npm } = detectPackageManager(); const generate = async (ctx: C3Context) => { - await runFrameworkGenerator(ctx, [ctx.project.name, "--ssr"]); + await runFrameworkGenerator(ctx, [ + ctx.project.name, + "--ssr", + "--server-routing" /** Dev Preview API */, + ]); logRaw(""); }; @@ -50,12 +54,21 @@ async function updateAppCode() { writeFile(resolve(appConfigPath), newAppConfig); s.stop(`${brandColor(`updated`)} ${dim(appConfigPath)}`); + // Update an app server routes file to: + const appServerRoutesPath = "src/app/app.routes.server.ts"; + const appRoutes = readFile(resolve(appServerRoutesPath)); + const newAppRoutes = appRoutes.replace( + "RenderMode.Prerender", + "RenderMode.Server", + ); + writeFile(resolve(appServerRoutesPath), newAppRoutes); + s.stop(`${brandColor(`updated`)} ${dim(appServerRoutesPath)}`); + // Remove unwanted dependencies s.start(`Updating package.json`); const packageJsonPath = resolve("package.json"); const packageManifest = readJSON(packageJsonPath); - delete packageManifest["dependencies"]["@angular/ssr"]; delete packageManifest["dependencies"]["express"]; delete packageManifest["devDependencies"]["@types/express"]; @@ -70,6 +83,8 @@ function updateAngularJson(ctx: C3Context) { // Update builder const architectSection = angularJson.projects[ctx.project.name].architect; architectSection.build.options.outputPath = "dist"; + architectSection.build.options.outputMode = "server"; + architectSection.build.options.ssr.experimentalPlatform = "neutral"; architectSection.build.options.assets.push("src/_routes.json"); writeFile(resolve("angular.json"), JSON.stringify(angularJson, null, 2)); @@ -93,8 +108,7 @@ const config: TemplateConfig = { scripts: { start: `${npm} run build && wrangler pages dev dist/cloudflare ${await compatDateFlag()} --experimental-local`, build: `ng build && ${npm} run process`, - process: - "node ./tools/copy-files.mjs && node ./tools/alter-polyfills.mjs", + process: "node ./tools/copy-files.mjs", deploy: `${npm} run build && wrangler pages deploy dist/cloudflare`, }, }), diff --git a/packages/create-cloudflare/templates/angular/templates/server.ts b/packages/create-cloudflare/templates/angular/templates/server.ts deleted file mode 100644 index dedcbe744d60..000000000000 --- a/packages/create-cloudflare/templates/angular/templates/server.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { renderApplication } from "@angular/platform-server"; -import bootstrap from "./src/main.server"; - -interface Env { - ASSETS: { fetch: typeof fetch }; -} - -// We attach the Cloudflare `fetch()` handler to the global scope -// so that we can export it when we process the Angular output. -// See tools/bundle.mjs -async function workerFetchHandler(request: Request, env: Env) { - const url = new URL(request.url); - console.log("render SSR", url.href); - - // Get the root `index.html` content. - const indexUrl = new URL("/", url); - const indexResponse = await env.ASSETS.fetch(new Request(indexUrl)); - const document = await indexResponse.text(); - - const content = await renderApplication(bootstrap, { - document, - url: url.pathname, - }); - - // console.log("rendered SSR", content); - return new Response(content, indexResponse); -} - -export default { - fetch: (request: Request, env: Env) => - (globalThis as any)["__zone_symbol__Promise"].resolve( - workerFetchHandler(request, env) - ), -}; diff --git a/packages/create-cloudflare/templates/angular/templates/src/server.ts b/packages/create-cloudflare/templates/angular/templates/src/server.ts new file mode 100644 index 000000000000..3bc8ccc23f9a --- /dev/null +++ b/packages/create-cloudflare/templates/angular/templates/src/server.ts @@ -0,0 +1,15 @@ +import { AngularAppEngine, createRequestHandler } from '@angular/ssr'; + +const angularApp = new AngularAppEngine(); + +/** + * This is a request handler used by the Angular CLI (dev-server and during build). + */ +export const reqHandler = createRequestHandler(async (req) => { + const res = await angularApp.render(req); + + return res ?? new Response('Page not found.', { status: 404 }); +}); + + +export default { fetch: reqHandler }; diff --git a/packages/create-cloudflare/templates/angular/templates/tools/alter-polyfills.mjs b/packages/create-cloudflare/templates/angular/templates/tools/alter-polyfills.mjs deleted file mode 100644 index eb479826900b..000000000000 --- a/packages/create-cloudflare/templates/angular/templates/tools/alter-polyfills.mjs +++ /dev/null @@ -1,27 +0,0 @@ -import fs from "node:fs"; -import { EOL } from "node:os"; -import { join } from "node:path"; -import { worker } from "./paths.mjs"; - -/** - * Split by lines and comment the banner - * ``` - * import { createRequire } from 'node:module'; - * globalThis['require'] ??= createRequire(import.meta.url); - * ``` - */ -const serverPolyfillsFile = join(worker, "polyfills.server.mjs"); -const serverPolyfillsData = fs - .readFileSync(serverPolyfillsFile, "utf8") - .split(/\r?\n/); - -for (let index = 0; index < 2; index++) { - if (serverPolyfillsData[index].includes("createRequire")) { - serverPolyfillsData[index] = "// " + serverPolyfillsData[index]; - } -} - -// Add needed polyfills -serverPolyfillsData.unshift(`globalThis['process'] = {};`); - -fs.writeFileSync(serverPolyfillsFile, serverPolyfillsData.join(EOL)); diff --git a/packages/create-cloudflare/templates/angular/templates/tools/copy-files.mjs b/packages/create-cloudflare/templates/angular/templates/tools/copy-files.mjs index 1fba4ce08dd3..274b305bbcb2 100644 --- a/packages/create-cloudflare/templates/angular/templates/tools/copy-files.mjs +++ b/packages/create-cloudflare/templates/angular/templates/tools/copy-files.mjs @@ -1,7 +1,13 @@ // Copy the files over so that they can be uploaded by the pages publish command. import fs from "node:fs"; -import { join } from "node:path"; -import { client, cloudflare, ssr, worker } from "./paths.mjs"; +import { join, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const root = resolve(fileURLToPath(import.meta.url), "../../"); +const client = resolve(root, "dist/browser"); +const ssr = resolve(root, "dist/server"); +const cloudflare = resolve(root, "dist/cloudflare"); +const worker = resolve(cloudflare, "_worker.js"); fs.cpSync(client, cloudflare, { recursive: true }); fs.cpSync(ssr, worker, { recursive: true }); diff --git a/packages/create-cloudflare/templates/angular/templates/tools/paths.mjs b/packages/create-cloudflare/templates/angular/templates/tools/paths.mjs deleted file mode 100644 index 723b1acea1c6..000000000000 --- a/packages/create-cloudflare/templates/angular/templates/tools/paths.mjs +++ /dev/null @@ -1,9 +0,0 @@ -import path from "node:path"; -import { fileURLToPath } from "node:url"; - -const dirname = path.dirname(fileURLToPath(import.meta.url)); -export const root = path.resolve(dirname, ".."); -export const client = path.resolve(root, "dist/browser"); -export const ssr = path.resolve(root, "dist/server"); -export const cloudflare = path.resolve(root, "dist/cloudflare"); -export const worker = path.resolve(cloudflare, "_worker.js");