diff --git a/.changeset/little-suns-bathe.md b/.changeset/little-suns-bathe.md new file mode 100644 index 000000000000..c563e7937f9e --- /dev/null +++ b/.changeset/little-suns-bathe.md @@ -0,0 +1,5 @@ +--- +"create-cloudflare": patch +--- + +fix: update Angular experimental Workers + Assets template diff --git a/packages/create-cloudflare/e2e-tests/frameworks.test.ts b/packages/create-cloudflare/e2e-tests/frameworks.test.ts index 4b4e8f8997bc..74225553943e 100644 --- a/packages/create-cloudflare/e2e-tests/frameworks.test.ts +++ b/packages/create-cloudflare/e2e-tests/frameworks.test.ts @@ -58,6 +58,121 @@ function getFrameworkTests(opts: { }): Record { if (opts.experimental) { return { + docusaurus: { + unsupportedPms: ["bun"], + testCommitMessage: true, + unsupportedOSs: ["win32"], + timeout: LONG_TIMEOUT, + verifyDeploy: { + route: "/", + expectedText: "Dinosaurs are cool", + }, + verifyPreview: { + route: "/", + expectedText: "Dinosaurs are cool", + }, + flags: [`--package-manager`, pm], + promptHandlers: [ + { + matcher: /Which language do you want to use\?/, + input: [keys.enter], + }, + ], + }, + angular: { + testCommitMessage: true, + timeout: LONG_TIMEOUT, + unsupportedOSs: ["win32"], + unsupportedPms: ["bun"], + verifyDeploy: { + route: "/", + expectedText: "Congratulations! Your app is running.", + }, + verifyPreview: { + route: "/", + expectedText: "Congratulations! Your app is running.", + }, + flags: ["--style", "sass"], + }, + gatsby: { + unsupportedPms: ["bun", "pnpm"], + promptHandlers: [ + { + matcher: /Would you like to use a template\?/, + input: ["n"], + }, + ], + testCommitMessage: true, + timeout: LONG_TIMEOUT, + verifyDeploy: { + route: "/", + expectedText: "Gatsby!", + }, + verifyPreview: { + route: "/", + expectedText: "Gatsby!", + }, + }, + hono: { + testCommitMessage: false, + unsupportedOSs: ["win32"], + verifyDeploy: { + route: "/", + expectedText: "Hello Hono!", + }, + verifyPreview: { + route: "/", + expectedText: "Hello Hono!", + }, + promptHandlers: [ + { + matcher: /Do you want to install project dependencies\?/, + input: [keys.enter], + }, + ], + }, + qwik: { + promptHandlers: [ + { + matcher: /Yes looks good, finish update/, + input: [keys.enter], + }, + ], + testCommitMessage: true, + unsupportedOSs: ["win32"], + unsupportedPms: ["yarn"], + verifyDeploy: { + route: "/", + expectedText: "Welcome to Qwik", + }, + verifyPreview: { + route: "/", + expectedText: "Welcome to Qwik", + }, + verifyBuildCfTypes: { + outputFile: "worker-configuration.d.ts", + envInterfaceName: "Env", + }, + }, + remix: { + testCommitMessage: true, + timeout: LONG_TIMEOUT, + unsupportedPms: ["yarn"], + unsupportedOSs: ["win32"], + verifyDeploy: { + route: "/", + expectedText: "Welcome to Remix", + }, + verifyPreview: { + route: "/test", + expectedText: "C3_TEST", + }, + verifyBuildCfTypes: { + outputFile: "worker-configuration.d.ts", + envInterfaceName: "Env", + }, + flags: ["--typescript", "--no-install", "--no-git-init"], + }, next: { testCommitMessage: false, verifyBuildCfTypes: { @@ -74,6 +189,80 @@ function getFrameworkTests(opts: { }, unsupportedOSs: ["win32"], }, + nuxt: { + quarantine: true, + testCommitMessage: true, + timeout: LONG_TIMEOUT, + unsupportedOSs: ["win32"], + verifyDeploy: { + route: "/", + expectedText: "Welcome to Nuxt!", + }, + verifyPreview: { + route: "/test", + expectedText: "C3_TEST", + }, + verifyBuildCfTypes: { + outputFile: "worker-configuration.d.ts", + envInterfaceName: "Env", + }, + }, + solid: { + promptHandlers: [ + { + matcher: /Which template would you like to use/, + input: [keys.enter], + }, + { + matcher: /Use Typescript/, + input: [keys.enter], + }, + ], + testCommitMessage: true, + timeout: LONG_TIMEOUT, + unsupportedPms: ["npm", "yarn"], + unsupportedOSs: ["win32"], + verifyDeploy: { + route: "/", + expectedText: "Hello world", + }, + verifyPreview: { + route: "/", + expectedText: "Hello world", + }, + }, + svelte: { + promptHandlers: [ + { + matcher: /Which template would you like/, + input: [keys.enter], + }, + { + matcher: /Add type checking with Typescript/, + input: [keys.down, keys.enter], + }, + { + matcher: /What would you like to add to your project/, + input: [keys.enter], + }, + { + matcher: + /Which package manager do you want to install dependencies with/, + input: [keys.enter], + }, + ], + testCommitMessage: true, + unsupportedOSs: ["win32"], + unsupportedPms: ["npm"], + verifyDeploy: { + route: "/", + expectedText: "SvelteKit app", + }, + verifyPreview: { + route: "/test", + expectedText: "C3_TEST", + }, + }, }; } else { // These are ordered based on speed and reliability for ease of debugging diff --git a/packages/create-cloudflare/templates-experimental/angular/c3.ts b/packages/create-cloudflare/templates-experimental/angular/c3.ts index 9c7e3046b339..cbdd80278ab2 100644 --- a/packages/create-cloudflare/templates-experimental/angular/c3.ts +++ b/packages/create-cloudflare/templates-experimental/angular/c3.ts @@ -12,7 +12,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(""); }; @@ -23,9 +27,9 @@ const configure = async (ctx: C3Context) => { }; async function installCFWorker() { - await installPackages(["@cloudflare/workers-types", "wrangler"], { + await installPackages(["xhr2"], { dev: true, - startText: "Installing adapter dependencies", + startText: "Installing additional dependencies", doneText: `${brandColor("installed")} ${dim(`via \`${npm} install\``)}`, }); } @@ -46,12 +50,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"]; @@ -66,7 +79,8 @@ function updateAngularJson(ctx: C3Context) { // Update builder const architectSection = angularJson.projects[ctx.project.name].architect; architectSection.build.options.outputPath = "dist"; - architectSection.build.options.assets.push("src/_routes.json"); + architectSection.build.options.outputMode = "server"; + architectSection.build.options.ssr.experimentalPlatform = "neutral"; writeFile(resolve("angular.json"), JSON.stringify(angularJson, null, 2)); s.stop(`${brandColor(`updated`)} ${dim(`\`angular.json\``)}`); @@ -84,13 +98,13 @@ const config: TemplateConfig = { path: "templates-experimental/angular", devScript: "start", deployScript: "deploy", + previewScript: "start", generate, configure, transformPackageJson: async () => ({ scripts: { start: `${npm} run build && wrangler dev`, - build: `ng build && ${npm} run process`, - process: "node ./tools/alter-polyfills.mjs", + build: `ng build`, deploy: `${npm} run build && wrangler deploy`, }, }), diff --git a/packages/create-cloudflare/templates-experimental/angular/templates/server.ts b/packages/create-cloudflare/templates-experimental/angular/templates/server.ts deleted file mode 100644 index 406ee222615f..000000000000 --- a/packages/create-cloudflare/templates-experimental/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("/index.html", 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-experimental/angular/templates/src/server.ts b/packages/create-cloudflare/templates-experimental/angular/templates/src/server.ts new file mode 100644 index 000000000000..f75db0a749af --- /dev/null +++ b/packages/create-cloudflare/templates-experimental/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.handle(req); + + return res ?? new Response('Page not found.', { status: 404 }); +}); + + +export default { fetch: reqHandler }; diff --git a/packages/create-cloudflare/templates-experimental/angular/templates/tools/alter-polyfills.mjs b/packages/create-cloudflare/templates-experimental/angular/templates/tools/alter-polyfills.mjs deleted file mode 100644 index 327d301f30a6..000000000000 --- a/packages/create-cloudflare/templates-experimental/angular/templates/tools/alter-polyfills.mjs +++ /dev/null @@ -1,32 +0,0 @@ -import fs from "node:fs"; -import { EOL } from "node:os"; -import { join } from "node:path"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; -const dirname = path.dirname(fileURLToPath(import.meta.url)); - -/** - * Split by lines and comment the banner - * ``` - * import { createRequire } from 'node:module'; - * globalThis['require'] ??= createRequire(import.meta.url); - * ``` - */ -const serverPolyfillsFile = join( - dirname, - "../dist/server/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));