Skip to content

Instantly share code, notes, and snippets.

@gcoda
Last active February 15, 2022 13:10
Show Gist options
  • Save gcoda/5a48a8afd9377b0dd3523f68af512f98 to your computer and use it in GitHub Desktop.
Save gcoda/5a48a8afd9377b0dd3523f68af512f98 to your computer and use it in GitHub Desktop.
Cloudflare Worker Template

Cloudflare Worker Typescript Starter

Opinionated Template

init

  1. npx degit https://gist.github.com/5a48a8afd9377b0dd3523f68af512f98.git my-new-worker
  2. mkdir src; mv _src_worker.ts src/worker.ts
  3. npm install

Dev

Builds typescript. Bundles with webpack. Starts a local worker on 4321 port.

  • npm run dev

Deploy

  • npm run build
  • use wrangle.toml with webpack.config.js.
  • or ./bundle/worker.js as entry
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features
sourceType: 'module', // Allows for the use of imports
},
root: true,
env: { node: true, browser: true, serviceworker: true },
plugins: ['@typescript-eslint', 'prettier'],
extends: [
'plugin:@typescript-eslint/eslint-recommended',
'prettier/@typescript-eslint',
],
ignorePatterns: ['dist/', 'node_modules/'],
rules: {
// 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
// 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'array-callback-return': 'error',
complexity: ['error', { max: 5 }],
'max-lines-per-function': ['error', { max: 33 }],
'max-nested-callbacks': ['error', 3],
'max-depth': ['error', 4],
'max-statements': [
'error',
10,
{ ignoreTopLevelFunctions: true },
],
'consistent-return': 'error',
eqeqeq: ['error', 'always'],
'no-implicit-coercion': 'error',
'no-invalid-this': 'error',
'no-new-wrappers': 'error',
'no-return-assign': 'error',
'no-return-await': 'error',
'no-sequences': 'error',
'no-throw-literal': 'error',
'no-unused-expressions': 'error',
'no-useless-catch': 'error',
'no-useless-escape': 'error',
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
ignoreRestSiblings: true,
caughtErrors: 'none',
},
],
'no-useless-return': 'error',
'prefer-promise-reject-errors': 'error',
'require-await': 'error',
yoda: 'error',
camelcase: 'error',
'no-mixed-operators': 'error',
'prefer-const': 'error',
'prefer-template': 'error',
'template-curly-spacing': ['error', 'never'],
'func-names': ['error', 'always'],
'padding-line-between-statements': [
'error',
{ blankLine: 'always', prev: '*', next: 'return' },
],
'arrow-parens': [2, 'as-needed'],
},
}
node_modules
dist/
bundle/
addEventListener('fetch', (event: FetchEvent) => {
event.respondWith(new Response('Hello World'))
})
module.exports = {
// used for aliasing incompatible modules
}
{
"name": "cf-worker",
"version": "1.0.0",
"description": "",
"main": "dist/worker.js",
"scripts": {
"build": "tsc",
"build:watch": "tsc --watch",
"bundle": "webpack --info-verbosity verbose --progress",
"bundle:watch": "webpack --watch --info-verbosity verbose",
"bundle:serve": "node worker.serve.js",
"watch:all": "concurrently --handle-input -n typescript,webpack,server npm:build:watch npm:bundle:watch nodemon server:rs",
"dev": "export NODE_ENV=development && npm run build && npm run watch:all",
"test": "echo \"Error: no test specified\" && exit 1"
},
"nodemonConfig": {
"restartable": "rs",
"exec": "npm run bundle:serve",
"watch": [
"bundle/**/*.js"
],
"delay": "100",
"runOnChangeOnly": true,
"quiet": false
},
"keywords": [],
"author": "Yevhenii Ponomar <gcoding@gmail.com>",
"license": "ISC",
"dependencies": {
"optional-js": "^2.1.1"
},
"devDependencies": {
"@cloudflare/workers-types": "^1.0.6",
"@cloudflare/wrangler": "^1.6.0",
"@dollarshaveclub/cloudworker": "^0.1.2",
"@typescript-eslint/eslint-plugin": "^2.11.0",
"@typescript-eslint/parser": "^2.11.0",
"concurrently": "^5.0.0",
"eslint": "6.7.2",
"eslint-config-prettier": "^6.7.0",
"eslint-config-typescript": "^3.0.0",
"eslint-plugin-prettier": "^3.1.1",
"express": "^4.17.1",
"nodemon": "^1.19.4",
"prettier": "^1.19.1",
"raw-loader": "^3.1.0",
"terser-webpack-plugin": "^2.2.1",
"types-cloudflare-worker": "^1.0.1",
"typescript": "^3.7.2",
"webpack": "^4.41.2",
"webpack-bundle-analyzer": "^3.6.0",
"webpack-cli": "^3.3.9"
}
}
module.exports = {
arrowParens:
'avoid',
bracketSpacing: true,
jsxSingleQuote: true,
printWidth: 60,
semi: false,
singleQuote: true,
tabWidth: 2,
trailingComma:
'es5',
}
{
"compilerOptions": {
"preserveWatchOutput": true,
"incremental": true,
"baseUrl": "./src",
"paths": {
"@/*": [
"./*"
]
},
"resolveJsonModule": true,
"composite": false,
"target": "es2016",
"module": "commonjs",
"moduleResolution": "node",
"esModuleInterop": true,
"sourceMap": true,
"declaration": true,
"declarationMap": true,
"removeComments": true,
"strict": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"strictNullChecks": true,
"noErrorTruncation": true,
"noUnusedLocals": true,
"forceConsistentCasingInFileNames": true,
"types": [
"@cloudflare/workers-types"
],
"lib": [
"es2016",
"WebWorker",
],
"outDir": "dist"
},
"include": [
"@cloudflare/workers-types",
"src/**/*",
"src/*"
],
"exclude": [
"node_modules",
"node_modules/**",
"dist/**/*",
"dist/*"
]
}
const path = require('path')
const webpack = require('webpack')
const { NODE_ENV = 'production' } = process.env
const isProduction = NODE_ENV === 'production'
const TerserPlugin = require('terser-webpack-plugin')
console.log(` Build mode: ${NODE_ENV}`)
const nullModule = path.resolve(__dirname, './null.js')
module.exports = {
target: 'webworker',
watchOptions: {
aggregateTimeout: 1000,
poll: 1000,
ignored: ['node_modules', /\.ts$/],
},
//
performance: { hints: 'warning' },
mode: NODE_ENV,
devtool: 'source-map',
entry: './dist/worker.js',
output: {
path: path.resolve(__dirname, 'bundle'),
filename: 'worker.js',
},
target: 'webworker',
externals: {
'isomorphic-fetch': 'fetch',
'node-fetch': 'fetch',
console: 'console',
},
resolve: {
symlinks: true,
alias: { fs: nullModule },
},
plugins: [
new webpack.EnvironmentPlugin({
NODE_ENV: 'production', // use 'production' unless process.env.NODE_ENV is defined
}),
],
optimization: {
usedExports: true,
// /* // uncomment to disable XD
...(isProduction
? {
minimizer: [
new TerserPlugin({
cache: false,
parallel: true,
sourceMap: false,
terserOptions: {
output: { comments: false },
ecma: 8,
warnings: false,
parse: {},
compress: {
arguments: false,
arrows: true,
booleans: false,
booleans_as_integers: false,
collapse_vars: true,
comparisons: false,
computed_props: true,
conditionals: true,
dead_code: true,
defaults: true,
directives: true,
drop_console: isProduction,
drop_debugger: true,
ecma: 6,
evaluate: false,
expression: false,
global_defs: false,
hoist_funs: true,
hoist_props: true,
hoist_vars: false,
ie8: false,
if_return: true,
inline: true,
join_vars: true,
keep_classnames: false,
keep_fargs: true, // !!false Breaks Apollo federated schema
keep_fnames: false,
keep_infinity: false,
loops: true,
module: false,
negate_iife: true,
passes: 1,
properties: true,
pure_getters: 'strict',
pure_funcs: null,
reduce_funcs: true,
reduce_vars: false, // !! Breaks Apollo federated schema
sequences: true,
side_effects: true,
switches: true,
top_retain: null,
toplevel: false,
typeofs: true,
unsafe: false,
unsafe_arrows: false,
unsafe_comps: false,
unsafe_Function: false,
unsafe_math: false,
unsafe_methods: false,
unsafe_proto: false,
unsafe_regexp: false,
unsafe_undefined: true, //!
unused: true,
warnings: true,
},
mangle: {
properties: false, // !
eval: false, // (default false) -- Pass true to mangle names visible in scopes where eval or with are used.
keep_classnames: true, // (default false) -- Pass true to not mangle class names. Pass a regular expression to only keep class names matching that regex. See also: the keep_classnames compress option.
// !! keep_fargs: false Breaks Apollo federated schema
keep_fnames: true, // (default false) -- Pass true to not mangle function names. Pass a regular expression to only keep class names matching that regex. Useful for code relying on Function.prototype.name. See also: the keep_fnames compress option.
module: true, // (default false) -- Pass true an ES6 modules, where the toplevel scope is not the global scope. Implies toplevel.
reserved: [], // (default []) -- Pass an array of identifiers that should be excluded from mangling. Example: ["foo", "bar"].
toplevel: true, // (default false) -- Pass true to mangle names declared in the top level scope.
safari10: false, // (default false)
}, // Note `mangle.properties` is `false` by default.
},
}),
],
}
: {}),
/** */
},
}
const Cloudworker = require('@dollarshaveclub/cloudworker')
const fs = require('fs')
const { promisify } = require('util')
const { DEV_WORKER_PORT = 4321 } = process.env
const readFile = path =>
promisify(fs.readFile)(path, 'utf8')
readFile('./bundle/worker.js')
.then(workerBody => {
const cw = new Cloudworker(workerBody, {
debug: true,
// bindings: { KV: WorkerKV },
})
cw.listen(DEV_WORKER_PORT)
console.log({ DEV_WORKER_PORT })
})
.catch(e => console.trace(e.message))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment