
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title><![CDATA[ The Cloudflare Blog ]]></title>
        <description><![CDATA[ Get the latest news on how products at Cloudflare are built, technologies used, and join the teams helping to build a better Internet. ]]></description>
        <link>https://blog.cloudflare.com</link>
        <atom:link href="https://blog.cloudflare.com/" rel="self" type="application/rss+xml"/>
        <language>en-us</language>
        <image>
            <url>https://blog.cloudflare.com/favicon.png</url>
            <title>The Cloudflare Blog</title>
            <link>https://blog.cloudflare.com</link>
        </image>
        <lastBuildDate>Sat, 04 Apr 2026 05:37:28 GMT</lastBuildDate>
        <item>
            <title><![CDATA[Race ahead with Cloudflare Pages build caching]]></title>
            <link>https://blog.cloudflare.com/race-ahead-with-build-caching/</link>
            <pubDate>Thu, 28 Sep 2023 13:00:57 GMT</pubDate>
            <description><![CDATA[ Unleash the fast & furious in your builds with Cloudflare Pages' build caching. Reduce build times by caching previously computed project components. Now in Beta for select frameworks and package managers. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Today, we are thrilled to release a beta of Cloudflare Pages support for build caching! With build caching, we are offering a supercharged Pages experience by helping you cache parts of your project to save time on subsequent builds.</p><p>For developers, time is not just money – it’s innovation and progress. When every second counts in crunch time before a new launch, the “need for speed” becomes <i>critical</i>. With Cloudflare Pages’ built-in <a href="https://www.cloudflare.com/learning/serverless/glossary/what-is-ci-cd/">continuous integration and continuous deployment (CI/CD)</a>, developers count on us to drive fast. We’ve already taken great strides in making sure we’re enabling quick development iterations for our users by <a href="/cloudflare-pages-build-improvements/">making solid improvements on the stability and efficiency</a> of our build infrastructure. But we always knew there was more to our build story.</p>
    <div>
      <h3>Quick pit stops</h3>
      <a href="#quick-pit-stops">
        
      </a>
    </div>
    <p>Build times can feel like a developer's equivalent of a time-out, a forced pause in the creative process—the inevitable pit stop in a high-speed formula race.</p><p>Long build times not only breaks the flow of individual developers, but it can also create a ripple effect across the team. It can slow down iterations and push back deployments. In the fast-paced world of CI/CD, these delays can drastically impact productivity and the delivery of products.</p><p>We want to empower developers to <b>win the race</b>, miles ahead of competition.</p>
    <div>
      <h3>Mechanics of build caching</h3>
      <a href="#mechanics-of-build-caching">
        
      </a>
    </div>
    <p>At its core, build caching is a mechanism that stores artifacts of a build, allowing subsequent builds to reuse these artifacts rather than recomputing them from scratch. By leveraging the cached results, build times can be significantly reduced, leading to a more efficient build process.</p><p>Previously, when you initiated a build, the Pages CI system would generate every step of the build process, even if most parts of the codebase remain unchanged between builds. This is the equivalent to changing out every single part of the car during a pit stop, irrespective of if anything needs replacing.</p><p>Build caching refines this process. Now, the Pages build system will detect if cached artifacts can be leveraged, restore the artifacts, then focus on only computing the modified sections of the code. In essence, build caching acts like an experienced pit crew, smartly skipping unnecessary steps and focusing only on what's essential to get you back in the race faster.</p>
    <div>
      <h3>What are we caching?</h3>
      <a href="#what-are-we-caching">
        
      </a>
    </div>
    <p>It boils down to two components: dependencies and build output.</p><p>The Pages build system supports dependency caching for select package managers and build output caching for select frameworks. Check out our <a href="https://developers.cloudflare.com/pages/platform/build-caching">documentation</a> for more information on what’s currently supported and what’s coming up.</p><p>Let’s take a closer look at what exactly we are caching.</p><p><b>Dependencies:</b> upon initiating a build, the Pages CI system checks for cached artifacts from previous builds. If it identifies a cache hit for dependencies, it restores from cache to speed up dependency installation.</p><p><b>Build output:</b> if a cache hit for build output is identified, Pages will only build the changed assets. This approach enables the long awaited <i>incremental builds</i> for supported JavaScript frameworks.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4kqmUJuLrUGc7vtXbDc4X6/3f1440dbf1ad3acef20a2b99c18d6d28/image2-26.png" />
            
            </figure>
    <div>
      <h3>Ready, set … go!</h3>
      <a href="#ready-set-go">
        
      </a>
    </div>
    <p>Build caching is now in beta, and ready for you to test drive!</p><p>In this release, the feature will support the node-based package managers <a href="https://www.npmjs.com/">npm</a>, <a href="https://yarnpkg.com/">yarn</a>, <a href="https://pnpm.io/">pnpm</a>, as well as <a href="https://bun.sh/">Bun</a>. We’ve also ensured compatibility with the most popular frameworks that provide native incremental building support: <a href="https://www.gatsbyjs.com/">Gatsby.js</a>, <a href="https://nextjs.org/">Next.js</a> and <a href="https://astro.build/">Astro</a> – and more to come!</p><p>For you as a Pages user, interacting with build caching will be seamless. If you are working with an existing project, simply navigate to your project’s settings to toggle on Build Cache.</p><p>When you push a code change and initiate a build using Pages CI, build caching will kick-start and do its magic in the background.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4hWz7Sh9wtk64c01cSnNjG/6967b9783a75f3fdfaaa10bd26884e0b/image4-17.png" />
            
            </figure>
    <div>
      <h3>“Cache” us on Discord</h3>
      <a href="#cache-us-on-discord">
        
      </a>
    </div>
    <p>Have questions? Join us on our <a href="https://discord.com/invite/cloudflaredev?event=1152163002502615050">Discord Server</a>. We will be hosting an “Ask Us Anything” <a href="https://discord.com/invite/cloudflaredev?event=1152163002502615050">session</a> on October 2nd where you can chat live with members of our team! Your feedback on this beta is invaluable to us, so after testing out build caching, don't hesitate to share your experiences! Happy building!</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6lavvh2PfpjlEbNV0YEuGB/8104fcccf6bf1243dfa113e940317f82/image3-32.png" />
            
            </figure><p></p> ]]></content:encoded>
            <category><![CDATA[Birthday Week]]></category>
            <category><![CDATA[Cloudflare Pages]]></category>
            <category><![CDATA[Beta]]></category>
            <category><![CDATA[Speed]]></category>
            <guid isPermaLink="false">5NhsEJJxtKlKawPWJmHWJm</guid>
            <dc:creator>Anni Wang</dc:creator>
            <dc:creator>Jacob Hands</dc:creator>
            <dc:creator>John Fawcett</dc:creator>
        </item>
        <item>
            <title><![CDATA[Introducing Direct Uploads for Cloudflare Pages]]></title>
            <link>https://blog.cloudflare.com/cloudflare-pages-direct-uploads/</link>
            <pubDate>Tue, 10 May 2022 13:01:06 GMT</pubDate>
            <description><![CDATA[ Pages now supports direct uploads to give you more power to build and iterate how you want and with the tools you want ]]></description>
            <content:encoded><![CDATA[ <p></p><p>With Pages, we are constantly looking for ways to improve the developer experience. One of the areas we are keen to focus on is removing any barriers to entry for our users regardless of their use case or existing set up. Pages is an all-in-one solution with an automated Continuous Integration (CI) pipeline to help you build and deploy your site with one commit to your projects’ repositories hosted on GitHub or GitLab.</p><p>However, we realize that this excluded repositories that used a source control provider that Pages didn’t yet support and required varying build complexities. Even though Pages continues to build first-class integrations – for example, we <a href="/cloudflare-pages-partners-with-gitlab/">added GitLab support</a> in November 2021 – there are numerous providers to choose from, some of which use `git` alternatives like <a href="https://subversion.apache.org/">SVN</a> or <a href="https://www.mercurial-scm.org/">Mercurial</a> for their version control systems. It’s also common for larger companies to self-host their project repositories, guarded by a mix of custom authentication and/or proxy protocols.</p><p>Pages needed a solution that worked regardless of the repository’s source location and accommodate build project’s complexity. Today, we’re thrilled to announce that Pages now supports direct uploads to give you more power to build and iterate how you want and with the tools you want.</p>
    <div>
      <h2>What are direct uploads?</h2>
      <a href="#what-are-direct-uploads">
        
      </a>
    </div>
    <p>Direct uploads enable you to push your build artifacts directly to Pages, side-stepping the automatic, done-for-you CI pipeline that Pages provides for GitHub and GitLab repositories. This means that connecting a Pages project to a git repository is optional. In fact, using git or any version control system is optional!</p><p>Today, you can bring your assets directly to Pages by dragging and dropping them into our dashboard or pushing them through Wrangler CLI. You also have the power to use your own CI tool whether that’s something like <a href="http://developers.cloudflare.com/pages/how-to/use-direct-upload-with-continuous-integration/">GitHub Actions or CircleCI</a> to handle your build. Taking your output directory you can bring these files directly to Pages to create a new project and all subsequent deployments after that. Every deployment will be distributed right to the Cloudflare network within seconds.</p>
    <div>
      <h2>How does it work?</h2>
      <a href="#how-does-it-work">
        
      </a>
    </div>
    <p>After using your preferred CI tooling outside of Pages, there are two ways to bring your pre-built assets and create a project with the direct uploads feature:</p><ol><li><p>Use the Wrangler CLI</p></li><li><p>Drag and drop them into the Pages interface</p></li></ol>
    <div>
      <h3>Wrangler CLI</h3>
      <a href="#wrangler-cli">
        
      </a>
    </div>
    <p>With an estimated 43k weekly Wrangler downloads, you too can use it to iterate quickly on your Pages projects right through the command line. With Wrangler (now with brand-new <a href="/10-things-I-love-about-wrangler">updates</a>!), you can both create your project and new deployments with a single command.</p><p>After Wrangler is installed and authenticated with your Cloudflare account, you can execute the following command to get your site up and running:</p>
            <pre><code>npx wrangler pages publish &lt;directory&gt;</code></pre>
            <div></div>
<p></p><p>Integration with Wrangler provides not only a great way to publish changes in a fast and consecutive manner, but also enables a seamless workflow between CI tooling for building right to Pages for deployment. Check out our tutorials on using <a href="http://developers.cloudflare.com/pages/how-to/use-direct-upload-with-continuous-integration/">CircleCI and GitHub Actions</a> with Pages!</p>
    <div>
      <h3>Drag and drop</h3>
      <a href="#drag-and-drop">
        
      </a>
    </div>
    <p>However, we realize that sometimes you just want to get your site deployed instantaneously without any additional set up or installations. In fact, getting started with Pages shouldn’t have to require extensive configuration. The drag and drop feature allows you to take your pre-built assets and virtually drag them onto the Pages UI. With either a zip file or a single folder of assets, you can watch your project deploy in just a few short seconds straight to the 270+ cities in our network.</p><div></div>
<p></p>
    <div>
      <h2>What can you build?</h2>
      <a href="#what-can-you-build">
        
      </a>
    </div>
    <p>With this ease of deploying projects, the possibilities of what you can build are still endless. You can enjoy the fruits of Pages in a project created with direct uploads including but not limited to unique preview URLs, integration with Workers, Access and Web Analytics, and custom redirects/headers.</p><p>In thinking about your developer setup, direct uploads provide the flexibility to build the way you want such as:</p><ul><li><p>Designing and building your own CI workflow</p></li><li><p>Utilizing the CI tooling of your choice</p></li><li><p>Accommodating complex monorepo structures</p></li><li><p>Implementing custom CI logic for your builds.</p></li></ul>
    <div>
      <h2>Migrating from Workers Sites</h2>
      <a href="#migrating-from-workers-sites">
        
      </a>
    </div>
    <p>We’ll have to admit, the idea of publishing assets directly to our network came from a sister product to Pages called <a href="/workers-sites/">Workers Sites</a> and the resemblance is striking! However, Pages affords many feature enhancements to the developer experience that show as a pain point on Workers Sites.</p><p>With Pages direct uploads, you can enjoy the freedom and flexibility of customizing your workflow that Workers Sites provides while including an interface to track and share changes and manage production/preview environments. <a href="https://developers.cloudflare.com/pages/migrations/migrating-from-workers/">Check out our tutorial</a> on how to migrate over from Workers Sites.</p><p>This release immediately unlocks a broad range of use cases, allowing the most basic of projects to the most advanced to start deploying their websites to Pages today. Refer to our <a href="https://developers.cloudflare.com/pages/platform/direct-upload/">developer documentation</a> for more technical details. As always, head over to the <a href="https://discord.gg/cloudflaredev">Cloudflare Developers Discord</a> server and let us know what you think in the #direct-uploads-beta channel.</p>
    <div>
      <h2>Join us at Cloudflare Connect!</h2>
      <a href="#join-us-at-cloudflare-connect">
        
      </a>
    </div>
    <p>Calling all New York City developers! If you’re interested in learning more about Cloudflare Pages, join us for a series of workshops on how to build a full stack application on Thursday, May 12th. Follow along with demonstrations of using Pages alongside other products like Workers, Images and Cloudflare Gateway, and hear directly from our product managers. <a href="https://events.www.cloudflare.com/flow/cloudflare/connect2022nyc/landing/page/page">Register now</a>!</p> ]]></content:encoded>
            <category><![CDATA[Platform Week]]></category>
            <category><![CDATA[Cloudflare Pages]]></category>
            <category><![CDATA[Product News]]></category>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[Wrangler]]></category>
            <guid isPermaLink="false">54zX3KuZvT6IeXghtXaDt6</guid>
            <dc:creator>Nevi Shah</dc:creator>
            <dc:creator>Greg Brimble</dc:creator>
            <dc:creator>John Fawcett</dc:creator>
            <dc:creator>Sid Chatterjee</dc:creator>
        </item>
        <item>
            <title><![CDATA[Using Webpack to bundle your Workers modules]]></title>
            <link>https://blog.cloudflare.com/using-webpack-to-bundle-workers/</link>
            <pubDate>Mon, 25 Jun 2018 14:16:14 GMT</pubDate>
            <description><![CDATA[ Throughout this post, we’ll use contrived examples, shaky metaphors, and questionably accurate weather predictions to explain how to bundle your Service Worker with Webpack. ]]></description>
            <content:encoded><![CDATA[ <p><i>A brief introduction to bundling your Service Worker scripts.</i></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4NwkPVtyHfmAOZMPI2Z4as/47366eb8eb56dc184584166cdaab3bff/photo-1523212727988-82c430c79c8e" />
            
            </figure><p>Photo by <a href="https://unsplash.com/@joyceromero?utm_source=ghost&amp;utm_medium=referral&amp;utm_campaign=api-credit">Joyce Romero</a> / <a href="https://unsplash.com/?utm_source=ghost&amp;utm_medium=referral&amp;utm_campaign=api-credit">Unsplash</a></p>
            <pre><code>// The simplest Service Worker: A passthrough script
addEventListener('fetch', event =&gt; {
  event.respondWith(fetch(event.request))
})</code></pre>
            <p>The code above is simple and sweet: when a request comes into one of Cloudflare’s data centers, passthrough to the origin server. There is <i>absolutely no need</i> for us to introduce any complex tooling or dependencies. Nevertheless, introduce we will! The problem is, once your script grows even just a little bit, you’ll be tempted to use <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import">JavaScript’s fancy new module system</a>. However, in doing so, you’ll have a little bit of trouble uploading your script via our API (we only accept a single JS file).</p><p>Throughout this post, we’ll use contrived examples, shaky metaphors, and questionably accurate weather predictions to explain how to bundle your Service Worker with <a href="https://webpack.js.org/">Webpack</a>.</p>
    <div>
      <h3>Webpack</h3>
      <a href="#webpack">
        
      </a>
    </div>
    <p>Let’s just say Webpack is a module bundler. That is, if you have code in multiple files, and you tie them together like this:</p>
    <div>
      <h4>app.js</h4>
      <a href="#app-js">
        
      </a>
    </div>
    
            <pre><code>// Import the CoolSocks class from dresser.js
import { CoolSocks } from './dresser'
import { FancyShoes } from './closet'</code></pre>
            <p>Then you can tell webpack to follow all of those import statements to produce a single file. This is useful because Service Workers running on Cloudflare need to be a single file as well.</p>
    <div>
      <h3>Show me the code</h3>
      <a href="#show-me-the-code">
        
      </a>
    </div>
    <p>Remember when I said something about predicting weather? Let’s build a worker with TypeScript that responds with the current weather.</p><p>Make sure to have <a href="https://nodejs.org">NodeJS</a> installed.</p>
            <pre><code># Make a new project directory
mkdir weather-worker
cd weather-worker
mkdir src dist
# Initialize project and install dependencies
npm init
npm install --save-dev \
  awesome-typescript-loader \
  typescript \
  webpack \
  webpack-cli \
  workers-preview
touch src/index.ts src/fetch-weather.ts webpack.config.js tsconfig.json</code></pre>
            <p>You should now have a file in your project called <code>package.json</code>. Add the following code to that file:</p>
            <pre><code>  "scripts": {
    "build": "webpack",
    "build:watch": "webpack --watch",
    "preview": "workers-preview &lt; dist/bundle.js"
  }</code></pre>
            <p>Now edit the following files to match what is shown:</p>
    <div>
      <h4>tsconfig.json</h4>
      <a href="#tsconfig-json">
        
      </a>
    </div>
    
            <pre><code>{
  "compilerOptions": {
    "module": "commonjs",
    "target": "esnext",
    "lib": ["es2015", "webworker"],
    "jsx": "react",
    "noImplicitAny": true,
    "preserveConstEnums": true,
    "outDir": "./dist",
    "moduleResolution": "node"
  },
  "include": ["src/*.ts", "src/**/*.ts", "src/*.tsx", "src/**/*.tsx"]
}</code></pre>
            
    <div>
      <h4>webpack.config.js</h4>
      <a href="#webpack-config-js">
        
      </a>
    </div>
    
            <pre><code>const path = require('path')

module.exports = {
  entry: {
    bundle: path.join(__dirname, './src/index.ts'),
  },

  output: {
    filename: 'bundle.js',
    path: path.join(__dirname, 'dist'),
  },

  mode: process.env.NODE_ENV || 'development',

  watchOptions: {
    ignored: /node_modules|dist|\.js/g,
  },

  devtool: 'cheap-module-eval-source-map',

  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.json'],
    plugins: [],
  },

  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: 'awesome-typescript-loader',
      },
    ],
  },
}</code></pre>
            <p>For newcomers, this file will seem incredibly cryptic. All I can say is just to accept it as magic for now. You’ll eventually understand what’s going on.</p><blockquote><p>A note about <code>devtool: 'cheap-module-eval-source-map'</code>. Specifying this type of sourcemap is fast, lightweight, and results in stacktraces much more representative of your source code. They’re not exact (yet!), but we’re getting there.</p></blockquote>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/18EkSrkR6zwUeYdaw5raii/bf076831c7dd2c8d485757721a21e9bb/Screen-Shot-2018-06-20-at-5.46.48-PM.png" />
            
            </figure><p><i>Cloudflare fiddle devtools uses source maps to point you to the correct source file. Click through to see the problematic line.</i></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/246fUhbfWAx68lusG2fhKb/47265b387877e2d339a7c00cd4815722/Screen-Shot-2018-06-20-at-5.47.52-PM.png" />
            
            </figure>
    <div>
      <h4>src/index.ts</h4>
      <a href="#src-index-ts">
        
      </a>
    </div>
    
            <pre><code>import { fetchWeather } from './fetch-weather'

addEventListener('fetch', (event: FetchEvent) =&gt; {
  event.respondWith(handleRequest(event.request))
})

async function handleRequest(request: Request) {
  const weather = await fetchWeather('austin')
  const body = `
    ${weather.location.city}, ${weather.location.region}&lt;br&gt;
    ${weather.item.condition.temp} ${weather.item.condition.text}
  `.trim()

  return new Response(body, {
    headers: { 'Content-Type': 'text/html' },
  })
}</code></pre>
            
    <div>
      <h4>src/fetch-weather.ts</h4>
      <a href="#src-fetch-weather-ts">
        
      </a>
    </div>
    
            <pre><code>/**
 * Fetch the current weather conditions and forecast for a particular location
 * @param location location string to fetch
 * @param unit temperature units (c or f)
 */
export async function fetchWeather(location: string, unit = 'f') {
  const url = `https://query.yahooapis.com/v1/public/yql?q=select *
  from weather.forecast
  where u='${unit}'
    AND woeid in (
      select woeid from geo.places(1)
      where text="${location}"
  )&amp;format=json`
    .split('\n')
    .join(' ')
    // yahoo's api doesn't like spaces unless they're encoded
    .replace(/\s/g, '%20')

  const res = await fetch(url)

  if (res.status &gt;= 400) {
    throw new Error('Bad response from server')
  }

  const result = await res.json()

  return result.query.results &amp;&amp; result.query.results.channel
}</code></pre>
            <p>Now simply run:</p>
            <pre><code>npm run build &amp;&amp; npm run preview</code></pre>
            <p>This ought to build your script and open a page very similar to:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5hNT2U1yxvTRfm5Cpa5cxR/4677b288db793b3023af2475671e48d9/Screen-Shot-2018-06-20-at-5.51.19-PM.png" />
            
            </figure><p>This is great, but instead of returning the weather for every single resource request, maybe we should only return the weather on pathnames that match a particular pattern. Something like:</p>
            <pre><code>GET /weather/:city
GET /weather/austin
GET /weather/toronto</code></pre>
            <p>In that pattern, <code>city</code> is a variable. Anything that starts with <code>/weather/</code> will match, and everything after will be our city. This shouldn’t match a path like <code>/weather/austin/tatious</code>. Luckily there are off-the-shelf solutions on npm to handle exactly this sort of logic.</p>
    <div>
      <h3>Loading node modules</h3>
      <a href="#loading-node-modules">
        
      </a>
    </div>
    <p>Webpack also understands how to import npm modules into your bundle. To illustrate this, we’re going to use the fantastic <a href="https://www.npmjs.com/package/path-to-regexp">path-to-regexp</a> module.</p><p>Install and save the module:</p>
            <pre><code>npm install -S path-to-regexp</code></pre>
            <p>The path-to-regexp module converts the url path pattern <code>/weather/:city</code> to a regular expression. Using that regular expression, we can extract the variable <code>city</code> out of a pathname string. For instance, in the string ‘/weather/toronto’, the city variable is ‘toronto’. However, for the string ‘/users/123’, there is no match at all.</p><p>Let’s modify our <code>src/index.ts</code> file to include this new routing logic.</p>
    <div>
      <h4>src/index.ts</h4>
      <a href="#src-index-ts">
        
      </a>
    </div>
    
            <pre><code>import * as pathToRegExp from 'path-to-regexp'
import { fetchWeather } from './fetch-weather'

type TWeatherRequestParams = { city: string }
const weatherPath = '/weather/:city'

addEventListener('fetch', (event: FetchEvent) =&gt; {
  // Create a regular expression based on the pathname of the request
  const weatherPathKeys: pathToRegExp.Key[] = []
  const weatherRegex = pathToRegExp(weatherPath, weatherPathKeys)
  const url = new URL(event.request.url)
  const result = weatherRegex.exec(url.pathname)

  // No result, return early and passthrough
  if (!Array.isArray(result)) return

  // Build the request parameters object
  const params = weatherPathKeys.reduce(
    (params, key, i) =&gt; {
      params[key.name as keyof TWeatherRequestParams] = result[i + 1]
      return params
    },
    {} as TWeatherRequestParams,
  )

  event.respondWith(handleWeatherRequest(params))
})

async function handleWeatherRequest(params: TWeatherRequestParams) {
  const weather = await fetchWeather(params.city)
  const body = `
    ${weather.location.city}, ${weather.location.region}&lt;br&gt;
    ${weather.item.condition.temp} ${weather.item.condition.text}
  `.trim()

  return new Response(body, {
    headers: { 'Content-Type': 'text/html' },
  })
}</code></pre>
            <p>Notice that after installing the module, all we have to do is import by its name on npm. This is because webpack knows to look inside of your node_modules directory to resolve import statement paths.</p><p>Run:</p>
            <pre><code>npm run build &amp;&amp; npm run preview -- \
  --preview-url https://foo.bar.com/weather/austin</code></pre>
            <p>You should see the weather for Austin, TX displayed. Congrats!</p>
    <div>
      <h3>Conclusion</h3>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>Webpack is an incredibly robust and configurable piece of technology. It’s not just a module bundler, but rather a general static asset build system for web development. Although it was built with front-end assets in mind, it’s a perfect fit for bundling Cloudflare Service Workers.</p><p>You can view the full source of our weather script here: <a href="https://github.com/jrf0110/weather-workers">github.com/jrf0110/weather-workers</a></p><hr /><p><i>If you have a worker you'd like to share, or want to check out workers from other Cloudflare users, visit the </i><a href="https://community.cloudflare.com/tags/recipe-exchange"><i>“Recipe Exchange”</i></a><i> in the Workers section of the </i><a href="https://community.cloudflare.com/c/developers/workers"><i>Cloudflare Community Forum</i></a><i>.</i></p> ]]></content:encoded>
            <category><![CDATA[Developer Platform]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[JavaScript]]></category>
            <category><![CDATA[Developers]]></category>
            <guid isPermaLink="false">3Cig5qNm9MxILXx94cjpFy</guid>
            <dc:creator>John Fawcett</dc:creator>
        </item>
        <item>
            <title><![CDATA[Generating Documentation for TypeScript Projects]]></title>
            <link>https://blog.cloudflare.com/generating-documentation-for-typescript-projects/</link>
            <pubDate>Sat, 12 Nov 2016 13:00:00 GMT</pubDate>
            <description><![CDATA[ Traditionally, JavaScript docs use code comments. This post explores generating documentation directly from TypeScript source code, a more efficient alternative. ]]></description>
            <content:encoded><![CDATA[ <p>Documentation for JavaScript projects has traditionally been generated via annotations inserted as code comments. While this gets the job done, it seems far from ideal. In this post, I’ll explore how to use TypeScript to generate documentation from source code alone.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3w7jaH3B0aNtw8NPhF6CPm/8af4c055c48aa7b1a2ffcdaf341117bf/511102367_1ce398ad1d_o.png" />
          </figure><p><a href="https://creativecommons.org/licenses/by-sa/2.0/"><sub>CC BY-SA 2.0</sub></a><sub> </sub><a href="https://www.flickr.com/photos/davidjoyner/511102367/in/photolist-MawVM-7zyucG-7aZ4QT-6GBfU4-aFrGn6-unAh43-axBGXx-ciiroq-92cKAD-iER1r2-5bRGPh-92fVBf-92fQBm-iER1Gc-2JMg5k-8rfzDR-iERFKo-jL2WXi-iEU2RU-jL4n6N-nqbf3-iER2q6-6H9k7G-dgnntH-eMMtEx-hHiLuM-9Sx4wb-9pCef2-9RLkVe-iEU1Nb-jL27Jr-ai89xS-dWQYFd-dyZqij-7cnSxN-8dyQSg-6yATnF-3om4q-nMWdCg-2KEjsj-eoQ3Lw-eoQfdN-9snnkJ-abB6Hb-2KEvib-eoQcXG-iEU2xh-9Hz7sB-5VhaNf-2KzRJp"><sub>image</sub></a><sub> by </sub><a href="https://www.flickr.com/photos/davidjoyner/"><sub>David Joyner</sub></a></p><p><a href="https://www.typescriptlang.org/">TypeScript</a> is JavaScript with optional types. Here’s a simple example:</p>
            <pre><code>// Sends some data to some analytics endpoint
function sendAnalyticsJS(data) {
  if (typeof data.type !== 'string') {
    throw new Error('The `type` property is required')
  }

  navigator.sendBeacon('/beacon', JSON.stringify(data))
}


// Results in run-time error
//    The `type` property is required
sendAnalyticsJS({ foo: 'bar' })</code></pre>
            <p>The JavaScript code will result in a run-time error. This is fine if the developer catches it early, but it would be better if the developer were warned as the bug was introduced. Here’s the same code written using TypeScript:</p>
            <pre><code>// Describe the shape of the data parameter
interface IAnalyticsData {
  // Type is required
  type: string
  // All other fields are fair game
  [propName: string]: string
}


// We don’t particularly need the data.type check here since
// the compiler will stamp out the majority of those cases.
function sendAnalytics(data: IAnalyticsData) {
  if (typeof data.type !== 'string') {
    throw new Error('The `type` property is required')
  }

  navigator.sendBeacon('/beacon', JSON.stringify(data))
}


// Results in compile-time error:
//   Argument of type '{ foo: string; }' is not assignable to
//   parameter of type 'IAnalyticsData'.
//   Property 'type' is missing in type '{ foo: string; }'.
sendAnalytics({ foo: 'bar' })</code></pre>
            <p>These annotations are all optional and the more you add, the more the compiler can help you. Compiling the TypeScript version results in code equivalent to the first example. The only difference is that the developer is warned about an error while writing the code.</p><p>With TypeScript, JavaScript developers are given powerful tools that aid the development of applications, large and small. Anders Hejlsberg, lead architect of C# and core dev for TypeScript, describes the language as, “JavaScript that scales.”</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3jIOfMZV8iXfXxcMK7BE3k/d6a0cd9a3506b97173a9eb5c45dd3844/code-peek.gif" />
          </figure><p>Using TypeScript means you can:</p><ul><li><p>Interactively explore library interfaces from your text editor</p></li><li><p>Enjoy useful auto-completion</p></li><li><p>Use good <a href="https://www.cloudflare.com/learning/cloud/how-to-refactor-applications/">refactoring</a> tools</p></li><li><p>Navigate your codebase via the ontology that describes it (by that, I mean jumping to class and interface definitions, modules, etc. from individual instances/references)</p></li><li><p>And most importantly to this post, generate documentation that’s tightly coupled to your codebase.</p></li></ul>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4o7pH1ZjdsErMByLoT0rIo/20a9cdb37bb362904cef82980d290fb1/amp-viewer-docs-2.png" />
          </figure><p><sub><i>The screenshot above is of the generated documentation from a TypeScript project at Cloudflare.</i></sub></p>
    <div>
      <h3>Why not JSDoc?</h3>
      <a href="#why-not-jsdoc">
        
      </a>
    </div>
    
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6gM4NbM9ekay7QlBVJrifP/f6bb60a9c6d50eb2ade0172d616832c1/jsdoc-example-1.png" />
          </figure><p>The amount of work required to annotate source code with JSDoc comments is comparable to adopting TypeScript annotations. However, JSDoc comments are not tightly coupled to the codebase, so when the code changes, an independent change of the JSDoc comment is also required. Contrast to TypeScript where the structure is gleaned directly from the source. Here’s a side-by-side comparison between JSDoc and TypeScript:</p>
            <pre><code>/**
 * A class representing a point
 * @class  Point
 */
class Point {
  /**
   * Create a point.
   * @param {number} x - The x value.
   * @param {number} y - The y value.
   */
  constructor(x, y) {
    /**
     * The x coordinate
     * @name  Point#x
     * @type {number}
     */
    this.x = x
    /**
     * The y coordinate
     * @name  Point#y
     * @type {number}
     */
    this.y = y
  }


  /**
   * Get the x value.
   * @return {number} The x value.
   */
  getX() {
    return this.x
  }


  /**
   * Get the y value.
   * @return {number} The y value.
   */
  getY() {
    return this.y
  }


  /**
   * Convert a string containing two comma-separated numbers into a point.
   * @param {string} str - The string containing two comma-separated numbers.
   * @return {Point} A Point object.
   */
  static fromString(str) {
    const args = str.split(',').map(arg =&gt; +arg)
    return new Point(args[0], args[1])
  }
}</code></pre>
            <p><b>TypeScript</b></p>
            <pre><code>/** Class representing a point. */
class Point {
  /** The x coordinate */
  public x: number
  /** The x coordinate */
  public y: number


  /**
   * Create a point.
   * @param x - The x value.
   * @param y - The y value.
   */
  constructor(x: number, y: number) {
    this.x = x
    this.y = y
  }


  /**
   * Get the x value.
   */
  getX() {
    return this.x
  }


  /**
   * Get the y value.
   */
  getY() {
    return this.y
  }

  /**
   * Convert a string containing two comma-separated numbers into a point.
   * @param str - The string containing two comma-separated numbers.
   */
  static fromString(str: string): Point {
    const args = str.split(',').map(arg =&gt; +arg)
    return new Point(args[0], args[1])
  }
}</code></pre>
            <p>The above code sample was taken from the <a href="http://usejsdoc.org/howto-es2015-classes.html#documenting-a-simple-class">JSDoc documentation</a> and adapted for use with TypeScript.</p><p>The annotations for TypeScript are much more compact, they’re syntax-highlighted, and most importantly, if something is wrong, the compiler lets us know. Long-form descriptions of things are still made in comments, but the type information has been moved into language semantics.</p><p>The downside to adopting TypeScript is the large amount of work required to fit the build tools into your current processes. However, we won’t focus on the nitty-gritty details of build tools since the ecosystem is rapidly changing.</p>
    <div>
      <h3>Using TypeDoc</h3>
      <a href="#using-typedoc">
        
      </a>
    </div>
    <p>At Cloudflare, we use a tool called <a href="http://typedoc.org/">TypeDoc</a> to help build documentation. It’s set up such that documentation-generation is on watch and will re-build on codebase changes. Anybody hacking on the project will always have up-to-date docs at localhost:3000/docs.</p><p>If you’re using TypeScript 2.x, use the following command to install TypeDoc for your project:</p>
            <pre><code>npm install --save-dev https://github.com/DatenMetzgerX/typedoc/tarball/typescript-2-build</code></pre>
            <p>Otherwise, for prior versions of TypeScript, you can install straight from </p>
            <pre><code>npm install --save-dev typedoc</code></pre>
            <p>From within your project directory, run the following command:</p>
            <pre><code># Change the --out flag to wherever you’d like the output to be stored
./node_modules/.bin/typedoc --out dist/docs --mode modules .</code></pre>
            <p>You should see a bunch of HTML documents generated. One for each class and module.</p>
            <pre><code>dist/docs
├── assets
│   ├── css
│   │   ├── main.css
│   │   └── main.css.map
│   ├── images
│   │   ├── icons.png
│   │   ├── icons@2x.png
│   │   ├── widgets.png
│   │   └── widgets@2x.png
│   └── js
│       ├── main.js
│       └── search.js
├── classes
│   ├── _src_my_class.html
│   └── ...
├── globals.html
├── index.html
├── interfaces
│   ├── _src.my_interface.html
└── modules
    ├── _src_my_module_.html
    └── ...</code></pre>
            <p>I wanted to build something akin to <a href="https://lodash.com/docs">Lodash’s documentation</a> (a beautiful single page API reference with examples and links to source code). Luckily, you can use TypeDoc’s --json flag to produce a set of useful descriptions of your codebase in JSON format.</p>
            <pre><code>./node_modules/.bin/typedoc --json dist/docs.json --mode modules</code></pre>
            <p><i>Note: In order to use the --json flag, you’ll need to setup a proper </i><a href="https://www.typescriptlang.org/docs/handbook/tsconfig-json.html"><i>tsconfig.json</i></a><i> for your TypeScript project.</i></p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3xn0KEhoWHUw1oYZNtv7yX/d7107a0ca746db90d9fe50a8aa6e5e51/ts-docs-post-screen-1.png" />
          </figure><p>With this high-level, structured description of our code base, we can render any HTML we’d like with a few scripts and a templating language like <a href="http://handlebarsjs.com/">Handlebars</a>. Here’s a simple script to render a list of code base modules:</p>
            <pre><code>const fs = require('fs')
const hbs = require('handlebars')
const project = require('./dist/docs.json')


// The HTML template to use for our simple docs
const tmpl = `
&lt;!DOCTYPE HTML&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;title&gt;My Project Documentation&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;Modules&lt;/h1&gt;
    &lt;ul&gt;
    {{#each project.children}}
      &lt;li&gt;{{this.name}}&lt;/li&gt;
    {{/each}}
    &lt;/ul&gt;
  &lt;/body&gt;
&lt;/html&gt;
`


// Compile the template with handlebars using our project
// object as context key
const result = hbs.compile(tmpl)({ project })


fs.writeFileSync('dist/docs.html', result)</code></pre>
            <p>While this means that there is a lot of work up front to create a template that suits the needs of this particular code base, I hope to use the same infrastructure for TypeScript projects at Cloudflare moving forward.</p>
    <div>
      <h3>More Than API Reference</h3>
      <a href="#more-than-api-reference">
        
      </a>
    </div>
    <p>TypeDoc gets us halfway there. It provides a structured and automated way to create reference material that is always in sync with our codebase; but we can do more than reference material. Suppose you’re writing a getting-started.md file. You might say something like this:</p>
            <pre><code>To get started, call the `viewer.init()` method.</code></pre>
            <p>Since we’re using TypeDoc and Handlebars, we can assume we have all the information necessary to actually link to the source code. That might look something like this:</p>
            <pre><code>{{!-- Pull the AMPViewer class into scope --}}
{{#Class "AMPViewer"}}
{{!-- Pull the init member from AMPViewer into scope --}}
{{#Member "init"}}
{{!-- Link viewer.init(...) to its location in the source code --}}
{{!-- Also, if the function signature of the .init() method ever changes, --}}
{{!-- it will be reflected here --}}
To get started, call the
`viewer.[init({{&gt; signature signature=signatures.0}})]({{getSourceURL this.sources.0}})`
{{/Member}}
{{/Class}}</code></pre>
            <p>While the above looks arcane, it’s just Markdown and Handlebars. The template contains a block helper called Class that will pull the AMPViewer class object into the current scope. Then using the AMPViewer class, we pull the init member into the current scope. We use the {{&gt; signature}} partial to pretty-print the function’s signature and the getSourceUrl helper to link to this method in the source code.</p><p>If the source code changes, our docs update too.</p>
    <div>
      <h3>Open Source and Beyond</h3>
      <a href="#open-source-and-beyond">
        
      </a>
    </div>
    <p>Over next couple of months, we’ll be open sourcing the tools we’ve created to generate our documentation. We’ll also open source the theme shown in this post so you can generate docs without worrying about the styling. We hope to create a rich TypeScript ecosystem and to have fantastic documentation for all of our projects.</p><p>If you’re thinking of generating documentation by annotating your source, use TypeScript instead and enjoy the benefits.</p> ]]></content:encoded>
            <category><![CDATA[JavaScript]]></category>
            <category><![CDATA[AMP]]></category>
            <category><![CDATA[Open Source]]></category>
            <category><![CDATA[Programming]]></category>
            <guid isPermaLink="false">5ff63m4WxGcMQJHDnKARpr</guid>
            <dc:creator>John Fawcett</dc:creator>
        </item>
    </channel>
</rss>