@nuxtlib/cf-deployment 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +220 -0
- package/dist/module.d.mts +18 -0
- package/dist/module.json +12 -0
- package/dist/module.mjs +309 -0
- package/dist/runtime/server/tsconfig.json +3 -0
- package/dist/types.d.mts +3 -0
- package/package.json +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# `@nuxtlib/cf-deployment`
|
|
2
|
+
|
|
3
|
+
## Deploy your website to Cloudflare Workers with GitHub Actions in minutes, with multiple environments, configurations, and branches.
|
|
4
|
+
|
|
5
|
+
### 1. Install the module in your Nuxt project
|
|
6
|
+
```bash
|
|
7
|
+
npm install -D @nuxtlib/cf-deployment
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
### 2. Configure `cfDeployment` in `nuxt.config.ts`. (Minimal example)
|
|
11
|
+
```ts
|
|
12
|
+
export default defineNuxtConfig({
|
|
13
|
+
modules: ['@nuxtlib/cf-deployment'],
|
|
14
|
+
cfDeployment: {
|
|
15
|
+
appName: 'your-app-name',
|
|
16
|
+
environments: [
|
|
17
|
+
{ branch: 'main', url: 'example.com' },
|
|
18
|
+
{ branch: 'staging', url: 'staging.example.com' },
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
})
|
|
22
|
+
```
|
|
23
|
+
### 3. Cloudflare setup:
|
|
24
|
+
|
|
25
|
+
1. In Cloudflare Dashboard, go to `Workers & Pages` and copy your `Account ID`.
|
|
26
|
+

|
|
27
|
+
|
|
28
|
+
2. In Cloudflare, go to `API Tokens` -> `Create Token` -> choose `Edit Cloudflare Workers`, scope it to your account, create the token, and copy it.
|
|
29
|
+

|
|
30
|
+
|
|
31
|
+
#### If your domain isn't registered in Clouflare yet
|
|
32
|
+
Go to Cloudflare, add your domain and point nameservers to Cloudflare. Wait until the zone is `Active`.
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
### 4. GitHub setup:
|
|
36
|
+
1. In GitHub, open `Settings` -> `Secrets and variables` -> `Actions`, then add repository secrets `CLOUDFLARE_ACCOUNT_ID` and `CLOUDFLARE_API_TOKEN`.
|
|
37
|
+

|
|
38
|
+
2. Optional - In GitHub, go to your repository `Settings` -> `Environments`, then create the environments used by your deployment flow.
|
|
39
|
+
3. Optional - For each GitHub environment, configure deployment branch restrictions.
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
### Final touch and Verification (CLI or dashboard)
|
|
43
|
+
Push to a configured branch and enjoy automatic deployment.
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
#### Preview the deploy
|
|
47
|
+
|
|
48
|
+
- In GitHub -> `Actions`, you should see a run start when you push to one of your configured branches. It installs dependencies, builds Nuxt, then calls Wrangler. A green run means the Worker was deployed.
|
|
49
|
+
- In Cloudflare -> `Build` -> `Compute & AI` -> `Workers & Pages`, you should now see two Workers named from your `wrangler.toml` (for each of your environments).
|
|
50
|
+
- In Cloudflare -> `Domain` -> `DNS` -> `Records`, two DNS records should point to the Workers (one for each environment). They appear after deploy and confirm the routes are attached to your domains.
|
|
51
|
+
- Propagation can take a few minutes. Once complete, you can hit your environment URLs in the browser and see your deployed app.
|
|
52
|
+
|
|
53
|
+
#### CLI (real-time)
|
|
54
|
+
|
|
55
|
+
From your project root, stream logs for each Worker:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npx wrangler tail your-environment-name
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Hit your environment URL in the browser and you will see log entries in the terminal.
|
|
62
|
+
|
|
63
|
+
#### Dashboard
|
|
64
|
+
|
|
65
|
+
In Cloudflare -> `Workers & Pages` -> select your Worker -> use the `Logs` / `Observability` view to see requests and errors.
|
|
66
|
+
|
|
67
|
+
`cf-deployment` is a Cloudflare-first deployment layer for Nuxt apps.
|
|
68
|
+
It turns deployment from ad-hoc CI scripts into config-driven infrastructure generated from `cfDeployment` in `nuxt.config.ts`.
|
|
69
|
+
|
|
70
|
+
This module is for teams that want to stay deep in the Cloudflare ecosystem:
|
|
71
|
+
|
|
72
|
+
- Workers as the runtime
|
|
73
|
+
- Cloudflare routes/custom domains
|
|
74
|
+
- Cloudflare secrets and Wrangler environments
|
|
75
|
+
- GitHub Actions as the delivery pipeline
|
|
76
|
+
|
|
77
|
+
The goal is to keep deploy behavior explicit, repeatable, and versioned as your app and team scale.
|
|
78
|
+
|
|
79
|
+
## Why This Exists
|
|
80
|
+
|
|
81
|
+
Nuxt projects often begin with one environment and one deploy command, then quickly grow into:
|
|
82
|
+
|
|
83
|
+
- branch-based preview/staging/production flows
|
|
84
|
+
- multiple environment routes and naming rules
|
|
85
|
+
- package-manager-specific CI steps (`npm`, `pnpm`, or `yarn`)
|
|
86
|
+
- repeated edits to both `wrangler.toml` and workflow YAML
|
|
87
|
+
|
|
88
|
+
Without automation, those files drift apart. Branches deploy to wrong envs, route updates lag behind code, and CI behavior becomes inconsistent between repos.
|
|
89
|
+
|
|
90
|
+
`cf-deployment` solves that drift by generating deployment artifacts from one source of truth.
|
|
91
|
+
|
|
92
|
+
## Who It Is For
|
|
93
|
+
|
|
94
|
+
Use this module if your team:
|
|
95
|
+
|
|
96
|
+
- deploys Nuxt to Cloudflare Workers and wants config-as-code for deploy infrastructure
|
|
97
|
+
- needs reliable branch-to-environment mapping across `wrangler.toml` and GitHub Actions
|
|
98
|
+
- wants Cloudflare-native workflows without hand-maintaining YAML in every repository
|
|
99
|
+
- prefers owning deployment logic directly in the app repo instead of relying on hosted abstractions
|
|
100
|
+
|
|
101
|
+
It is especially useful for platform teams, agencies, and multi-project organizations that need consistent deploy behavior across many Nuxt services.
|
|
102
|
+
|
|
103
|
+
## Ecosystem Context
|
|
104
|
+
|
|
105
|
+
On July 8, 2025, Vercel announced that NuxtLabs (maintainers of Nuxt, Nitro, and NuxtHub) joined Vercel.
|
|
106
|
+
|
|
107
|
+
Nuxt remains open and deployable across providers, and many teams continue choosing Cloudflare for edge runtime, networking, and data products.
|
|
108
|
+
This module is built for that Cloudflare-first path: explicit Workers deploy control, reproducible CI, and infrastructure outputs you can audit in git.
|
|
109
|
+
|
|
110
|
+
## Cloudflare-First Strategy Fit
|
|
111
|
+
|
|
112
|
+
If your application stack is already centered on Cloudflare capabilities, deployment should follow the same model.
|
|
113
|
+
This module aligns deploy orchestration with that direction:
|
|
114
|
+
|
|
115
|
+
- compute at the edge with Workers
|
|
116
|
+
- domain/routing control through Cloudflare
|
|
117
|
+
- environment-aware Wrangler deploy targets
|
|
118
|
+
- CI-driven releases with explicit branch promotion rules
|
|
119
|
+
|
|
120
|
+
For teams standardizing on Cloudflare services like KV, R2, D1, Queues, or Durable Objects, keeping deploy control in-repo reduces cross-platform glue code and makes operational ownership clearer.
|
|
121
|
+
|
|
122
|
+
## What Problems It Solves
|
|
123
|
+
|
|
124
|
+
- Manual deploy config drift between app config, Wrangler, and CI
|
|
125
|
+
- Fragile branch deploy logic spread across copy-pasted workflow files
|
|
126
|
+
- Inconsistent install/build/deploy commands between package managers
|
|
127
|
+
- Hard-to-review infrastructure changes hidden in manual dashboard edits
|
|
128
|
+
- Growing operational overhead as environments and branches increase
|
|
129
|
+
|
|
130
|
+
## What It Generates
|
|
131
|
+
|
|
132
|
+
On every Nuxt setup build (`npx run dev`, `npx nuxt prepare`, `npx nuxt build`), the module rewrites:
|
|
133
|
+
|
|
134
|
+
- `wrangler.toml`
|
|
135
|
+
- `.github/workflows/deploy.yml`
|
|
136
|
+
|
|
137
|
+
This keeps Cloudflare config and GitHub Actions deploy automation in sync.
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
## Full configuration options example
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
export default defineNuxtConfig({
|
|
144
|
+
modules: ['cf-deployment'],
|
|
145
|
+
cfDeployment: {
|
|
146
|
+
// Required name - Used as the `name` field in `wrangler.toml` and as a prefix for environment names.
|
|
147
|
+
appName: 'my-app',
|
|
148
|
+
|
|
149
|
+
// Required at least one environment - Each item generates a Wrangler environment and GitHub Actions deploy step.
|
|
150
|
+
environments: [
|
|
151
|
+
|
|
152
|
+
// Your primary environment, deploying from the main branch to the root domain.
|
|
153
|
+
{ branch: 'main', url: 'app.example.com', samplingRate: 0.2 },
|
|
154
|
+
|
|
155
|
+
// Optional additional environments
|
|
156
|
+
{ branch: 'staging', url: 'staging.app.example.com', samplingRate: 1 },
|
|
157
|
+
|
|
158
|
+
// Optional additional environments
|
|
159
|
+
{ branch: 'feature-1', url: 'feature-1.app.example.com'},
|
|
160
|
+
],
|
|
161
|
+
|
|
162
|
+
// Optional [npm, pnpm, yarn]; default: 'npm' - Control how GitHub Actions install and deploy your app
|
|
163
|
+
packageManager: 'npm',
|
|
164
|
+
|
|
165
|
+
// Optional - Control the Node version used in GitHub Actions; default: '24'
|
|
166
|
+
nodeVersion: '24',
|
|
167
|
+
|
|
168
|
+
},
|
|
169
|
+
})
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
## Configuration
|
|
176
|
+
|
|
177
|
+
Set options under `cfDeployment` in `nuxt.config.ts`.
|
|
178
|
+
|
|
179
|
+
| Option | Required | Used in | Behavior |
|
|
180
|
+
| --- | --- | --- | --- |
|
|
181
|
+
| `appName` | No | `wrangler.toml` | Sets top-level `name` and prefixes each environment `name` |
|
|
182
|
+
| `environments` | No | Both | Drives Wrangler env blocks and workflow branch deploy targets |
|
|
183
|
+
| `environments[].branch` | Yes (per entry) | Both | Source branch and env key input |
|
|
184
|
+
| `environments[].url` | Yes (per entry) | `wrangler.toml` | Route pattern input (`https://...` or `host/path`) |
|
|
185
|
+
| `environments[].samplingRate` | No | `wrangler.toml` | Adds `[env.<name>.observability]` when valid (`0` to `1`) |
|
|
186
|
+
| `packageManager` | No | `deploy.yml` | `npm`, `pnpm`, `yarn`; invalid or empty falls back to `npm` |
|
|
187
|
+
| `nodeVersion` | No | `deploy.yml` | `actions/setup-node` version, default `24` |
|
|
188
|
+
|
|
189
|
+
## Generated Workflow Behavior
|
|
190
|
+
|
|
191
|
+
`deploy.yml` includes:
|
|
192
|
+
|
|
193
|
+
- `push` trigger
|
|
194
|
+
- Concurrency guard (`cancel-in-progress: true`)
|
|
195
|
+
- `CLOUDFLARE_ACCOUNT_ID` and `CLOUDFLARE_API_TOKEN` envs from GitHub secrets
|
|
196
|
+
- Package-manager-specific install/build/deploy commands
|
|
197
|
+
|
|
198
|
+
Branch behavior:
|
|
199
|
+
|
|
200
|
+
- With valid `environments[]`, `on.push.branches` is generated from those branches.
|
|
201
|
+
- Without valid `environments[]`, workflow runs on all pushes.
|
|
202
|
+
- With valid `environments[]`, one deploy step is generated per branch using `wrangler deploy --env <environmentName>`.
|
|
203
|
+
- Without valid `environments[]`, one generic deploy step is generated (`wrangler deploy`).
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
## Important Edge Cases
|
|
208
|
+
|
|
209
|
+
- Files are regenerated on each setup run; manual edits are overwritten.
|
|
210
|
+
- Invalid environment entries (missing `branch` or `url`) are skipped.
|
|
211
|
+
- Branch names are normalized for env keys: lowercased, non `[a-z0-9_]` characters become `_`.
|
|
212
|
+
- Duplicate normalized env keys get suffixes (`main`, `main_2`, `main_3`, ...).
|
|
213
|
+
- Route patterns remove scheme and trailing slash (`https://app.example.com/` -> `app.example.com`).
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
## Roadmap
|
|
217
|
+
- CF Resource binding generation (KV, R2, D1, Queues, Durable Objects)
|
|
218
|
+
- Domain provisioning
|
|
219
|
+
- Rollback strategies
|
|
220
|
+
- Improvement of internal files modularity and test coverage
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
+
|
|
3
|
+
interface DeploymentEnvironmentInput {
|
|
4
|
+
branch: string;
|
|
5
|
+
url: string;
|
|
6
|
+
samplingRate?: number;
|
|
7
|
+
}
|
|
8
|
+
interface ModuleOptions {
|
|
9
|
+
packageManager?: string;
|
|
10
|
+
appName?: string;
|
|
11
|
+
environments?: DeploymentEnvironmentInput[];
|
|
12
|
+
nodeVersion?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
16
|
+
|
|
17
|
+
export { _default as default };
|
|
18
|
+
export type { ModuleOptions };
|
package/dist/module.json
ADDED
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import { writeFile, mkdir } from 'node:fs/promises';
|
|
2
|
+
import { resolve, dirname } from 'node:path';
|
|
3
|
+
import { defineNuxtModule, useLogger } from '@nuxt/kit';
|
|
4
|
+
|
|
5
|
+
const DEFAULT_WORKFLOW_NAME = "Deploy to Cloudflare Workers";
|
|
6
|
+
const DEFAULT_WORKFLOW_PATH = ".github/workflows/deploy.yml";
|
|
7
|
+
const DEFAULT_NODE_VERSION = "24";
|
|
8
|
+
const DEFAULT_PACKAGE_MANAGER = "npm";
|
|
9
|
+
|
|
10
|
+
const DEFAULT_APP_NAME = "my-nuxt-app";
|
|
11
|
+
const DEFAULT_COMPATIBILITY_DATE = "2025-12-01";
|
|
12
|
+
const DEFAULT_ROUTE = "app.example.com";
|
|
13
|
+
|
|
14
|
+
function isCompleteEnvironment(environment) {
|
|
15
|
+
return Boolean(
|
|
16
|
+
typeof environment === "object" && environment && "branch" in environment && "url" in environment && typeof environment.branch === "string" && typeof environment.url === "string" && environment.branch.trim() && environment.url.trim()
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
function normalizeRoutePattern(input, fallback = DEFAULT_ROUTE) {
|
|
20
|
+
if (!input?.trim()) {
|
|
21
|
+
return fallback;
|
|
22
|
+
}
|
|
23
|
+
const rawValue = input.trim();
|
|
24
|
+
const urlValue = /^[a-z][\w+.-]*:\/\//i.test(rawValue) ? rawValue : `https://${rawValue}`;
|
|
25
|
+
try {
|
|
26
|
+
const parsedUrl = new URL(urlValue);
|
|
27
|
+
const normalizedPath = parsedUrl.pathname.replace(/\/+$/, "");
|
|
28
|
+
return `${parsedUrl.host}${normalizedPath}`;
|
|
29
|
+
} catch {
|
|
30
|
+
return rawValue.replace(/^[a-z][\w+.-]*:\/\//i, "").replace(/\/+$/, "");
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function toEnvironmentName(branch) {
|
|
34
|
+
const normalized = branch.trim().toLowerCase().replace(/[^a-z0-9_]+/g, "_").replace(/^_+|_+$/g, "");
|
|
35
|
+
return normalized || "env";
|
|
36
|
+
}
|
|
37
|
+
function toSamplingRate(value) {
|
|
38
|
+
if (typeof value !== "number" || Number.isNaN(value) || !Number.isFinite(value)) {
|
|
39
|
+
return void 0;
|
|
40
|
+
}
|
|
41
|
+
if (value < 0 || value > 1) {
|
|
42
|
+
return void 0;
|
|
43
|
+
}
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
function resolveEnvironments(environments, fallbackRoute = DEFAULT_ROUTE) {
|
|
47
|
+
const validEnvironments = (environments ?? []).filter(isCompleteEnvironment);
|
|
48
|
+
const usedEnvironmentNames = /* @__PURE__ */ new Set();
|
|
49
|
+
return validEnvironments.map((environment) => {
|
|
50
|
+
const branch = environment.branch.trim();
|
|
51
|
+
const routePattern = normalizeRoutePattern(environment.url, fallbackRoute);
|
|
52
|
+
const samplingRate = toSamplingRate(environment.samplingRate);
|
|
53
|
+
let name = toEnvironmentName(branch);
|
|
54
|
+
if (usedEnvironmentNames.has(name)) {
|
|
55
|
+
let suffix = 2;
|
|
56
|
+
while (usedEnvironmentNames.has(`${name}_${suffix}`)) {
|
|
57
|
+
suffix += 1;
|
|
58
|
+
}
|
|
59
|
+
name = `${name}_${suffix}`;
|
|
60
|
+
}
|
|
61
|
+
usedEnvironmentNames.add(name);
|
|
62
|
+
return {
|
|
63
|
+
branch,
|
|
64
|
+
name,
|
|
65
|
+
routePattern,
|
|
66
|
+
samplingRate
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const WORKFLOW_PACKAGE_MANAGERS = ["npm", "pnpm", "yarn"];
|
|
72
|
+
function normalizePackageManager(input) {
|
|
73
|
+
if (!input?.trim()) {
|
|
74
|
+
return DEFAULT_PACKAGE_MANAGER;
|
|
75
|
+
}
|
|
76
|
+
const normalized = input.trim().toLowerCase();
|
|
77
|
+
return WORKFLOW_PACKAGE_MANAGERS.includes(normalized) ? normalized : DEFAULT_PACKAGE_MANAGER;
|
|
78
|
+
}
|
|
79
|
+
function normalizeNodeVersion(input) {
|
|
80
|
+
return input?.trim() || DEFAULT_NODE_VERSION;
|
|
81
|
+
}
|
|
82
|
+
function resolveWorkflowTargets(environments) {
|
|
83
|
+
return resolveEnvironments(environments).map((environment) => ({
|
|
84
|
+
branch: environment.branch,
|
|
85
|
+
environmentName: environment.name
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
function resolveWorkflowOptions(options) {
|
|
89
|
+
return {
|
|
90
|
+
packageManager: normalizePackageManager(options.packageManager),
|
|
91
|
+
nodeVersion: normalizeNodeVersion(options.nodeVersion),
|
|
92
|
+
targets: resolveWorkflowTargets(options.environments)
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function escapeGithubExpressionString(value) {
|
|
97
|
+
return value.replaceAll("'", "''");
|
|
98
|
+
}
|
|
99
|
+
function getWorkflowHeaderBlock(branches) {
|
|
100
|
+
const lines = [
|
|
101
|
+
"# Generated by @nuxtlib/deployment-cf-workers",
|
|
102
|
+
`name: ${DEFAULT_WORKFLOW_NAME}`,
|
|
103
|
+
"",
|
|
104
|
+
"on:",
|
|
105
|
+
" push:"
|
|
106
|
+
];
|
|
107
|
+
if (branches.length > 0) {
|
|
108
|
+
lines.push(" branches:");
|
|
109
|
+
for (const branch of branches) {
|
|
110
|
+
lines.push(` - ${branch}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
lines.push(
|
|
114
|
+
"",
|
|
115
|
+
"concurrency:",
|
|
116
|
+
" group: deploy-${{ github.workflow }}-${{ github.ref }}",
|
|
117
|
+
" cancel-in-progress: true",
|
|
118
|
+
"",
|
|
119
|
+
"jobs:",
|
|
120
|
+
" deploy:",
|
|
121
|
+
" runs-on: ubuntu-latest",
|
|
122
|
+
"",
|
|
123
|
+
" env:",
|
|
124
|
+
" CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}",
|
|
125
|
+
" CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}",
|
|
126
|
+
"",
|
|
127
|
+
" steps:",
|
|
128
|
+
" - name: Checkout",
|
|
129
|
+
" uses: actions/checkout@v4"
|
|
130
|
+
);
|
|
131
|
+
return lines.join("\n");
|
|
132
|
+
}
|
|
133
|
+
function getBuildStepsBlock(packageManager, nodeVersion) {
|
|
134
|
+
if (packageManager === "pnpm") {
|
|
135
|
+
return [
|
|
136
|
+
" - name: Install pnpm",
|
|
137
|
+
" uses: pnpm/action-setup@v4",
|
|
138
|
+
" with:",
|
|
139
|
+
" version: 10",
|
|
140
|
+
"",
|
|
141
|
+
" - name: Setup Node",
|
|
142
|
+
" uses: actions/setup-node@v4",
|
|
143
|
+
" with:",
|
|
144
|
+
` node-version: ${nodeVersion}`,
|
|
145
|
+
" cache: pnpm",
|
|
146
|
+
" cache-dependency-path: pnpm-lock.yaml",
|
|
147
|
+
"",
|
|
148
|
+
" - name: Install deps",
|
|
149
|
+
" run: pnpm install --frozen-lockfile",
|
|
150
|
+
"",
|
|
151
|
+
" - name: Build Nuxt app",
|
|
152
|
+
" run: pnpm run build"
|
|
153
|
+
].join("\n");
|
|
154
|
+
}
|
|
155
|
+
if (packageManager === "yarn") {
|
|
156
|
+
return [
|
|
157
|
+
" - name: Setup Node",
|
|
158
|
+
" uses: actions/setup-node@v4",
|
|
159
|
+
" with:",
|
|
160
|
+
` node-version: ${nodeVersion}`,
|
|
161
|
+
" cache: yarn",
|
|
162
|
+
" cache-dependency-path: yarn.lock",
|
|
163
|
+
"",
|
|
164
|
+
" - name: Install deps",
|
|
165
|
+
" run: yarn install --frozen-lockfile",
|
|
166
|
+
"",
|
|
167
|
+
" - name: Build Nuxt app",
|
|
168
|
+
" run: yarn build"
|
|
169
|
+
].join("\n");
|
|
170
|
+
}
|
|
171
|
+
return [
|
|
172
|
+
" - name: Setup Node",
|
|
173
|
+
" uses: actions/setup-node@v4",
|
|
174
|
+
" with:",
|
|
175
|
+
` node-version: ${nodeVersion}`,
|
|
176
|
+
" cache: npm",
|
|
177
|
+
" cache-dependency-path: package-lock.json",
|
|
178
|
+
"",
|
|
179
|
+
" - name: Install deps",
|
|
180
|
+
" run: npm ci",
|
|
181
|
+
"",
|
|
182
|
+
" - name: Build Nuxt app",
|
|
183
|
+
" run: npm run build"
|
|
184
|
+
].join("\n");
|
|
185
|
+
}
|
|
186
|
+
function getDeployCommand(packageManager) {
|
|
187
|
+
if (packageManager === "pnpm") {
|
|
188
|
+
return "pnpm dlx wrangler deploy";
|
|
189
|
+
}
|
|
190
|
+
if (packageManager === "yarn") {
|
|
191
|
+
return "yarn dlx wrangler deploy";
|
|
192
|
+
}
|
|
193
|
+
return "npx wrangler deploy";
|
|
194
|
+
}
|
|
195
|
+
function getDeployStepsBlock(packageManager, targets) {
|
|
196
|
+
const deployCommand = getDeployCommand(packageManager);
|
|
197
|
+
if (targets.length === 0) {
|
|
198
|
+
return [
|
|
199
|
+
" - name: Deploy to Cloudflare",
|
|
200
|
+
` run: ${deployCommand}`
|
|
201
|
+
].join("\n");
|
|
202
|
+
}
|
|
203
|
+
return targets.map((target) => {
|
|
204
|
+
const escapedBranch = escapeGithubExpressionString(target.branch);
|
|
205
|
+
return [
|
|
206
|
+
` - name: Deploy to Cloudflare (${target.branch})`,
|
|
207
|
+
` if: github.ref == 'refs/heads/${escapedBranch}'`,
|
|
208
|
+
` run: ${deployCommand} --env ${target.environmentName}`
|
|
209
|
+
].join("\n");
|
|
210
|
+
}).join("\n\n");
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function renderWorkflowYaml(options) {
|
|
214
|
+
const resolvedOptions = resolveWorkflowOptions(options);
|
|
215
|
+
const branches = resolvedOptions.targets.map((target) => target.branch);
|
|
216
|
+
const lines = [
|
|
217
|
+
getWorkflowHeaderBlock(branches),
|
|
218
|
+
"",
|
|
219
|
+
getBuildStepsBlock(resolvedOptions.packageManager, resolvedOptions.nodeVersion),
|
|
220
|
+
"",
|
|
221
|
+
getDeployStepsBlock(resolvedOptions.packageManager, resolvedOptions.targets)
|
|
222
|
+
];
|
|
223
|
+
return lines.join("\n").trimEnd() + "\n";
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function escapeTomlString(value) {
|
|
227
|
+
return value.replaceAll("\\", "\\\\").replaceAll('"', '\\"');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function getNoEnvironmentSection() {
|
|
231
|
+
return `
|
|
232
|
+
# No environments found yet, so a default route is used.
|
|
233
|
+
routes = [{ pattern = "${escapeTomlString(DEFAULT_ROUTE)}", custom_domain = true }]
|
|
234
|
+
`.trim();
|
|
235
|
+
}
|
|
236
|
+
function getEnvironmentSection(appName, environment) {
|
|
237
|
+
const observabilityBlock = environment.samplingRate === void 0 ? "" : `
|
|
238
|
+
|
|
239
|
+
[env.${environment.name}.observability]
|
|
240
|
+
enabled = true
|
|
241
|
+
head_sampling_rate = ${environment.samplingRate}
|
|
242
|
+
`;
|
|
243
|
+
return `
|
|
244
|
+
# Branch: ${environment.branch}
|
|
245
|
+
[env.${environment.name}]
|
|
246
|
+
name = "${escapeTomlString(`${appName}-${environment.name}`)}"
|
|
247
|
+
routes = [
|
|
248
|
+
{ pattern = "${escapeTomlString(environment.routePattern)}", custom_domain = true }
|
|
249
|
+
]${observabilityBlock}
|
|
250
|
+
`.trim();
|
|
251
|
+
}
|
|
252
|
+
function getHeaderBlock(appName) {
|
|
253
|
+
return `
|
|
254
|
+
# Generated by @nuxtlib/deployment-cf-workers.
|
|
255
|
+
# This file is generated from your \`cfDeployment\` module config.
|
|
256
|
+
|
|
257
|
+
name = "${escapeTomlString(appName)}"
|
|
258
|
+
main = ".output/server/index.mjs"
|
|
259
|
+
compatibility_date = "${DEFAULT_COMPATIBILITY_DATE}"
|
|
260
|
+
`.trim();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function renderWranglerToml(options) {
|
|
264
|
+
const appName = options.appName?.trim() || DEFAULT_APP_NAME;
|
|
265
|
+
const environments = resolveEnvironments(options.environments);
|
|
266
|
+
const headerBlock = getHeaderBlock(appName);
|
|
267
|
+
const lines = [headerBlock];
|
|
268
|
+
lines.push("");
|
|
269
|
+
if (environments.length === 0) {
|
|
270
|
+
lines.push(getNoEnvironmentSection());
|
|
271
|
+
return lines.join("\n").trimEnd() + "\n";
|
|
272
|
+
}
|
|
273
|
+
lines.push("# One block is generated for each item in `cfDeployment.environments`.");
|
|
274
|
+
lines.push("");
|
|
275
|
+
for (const environment of environments) {
|
|
276
|
+
lines.push(getEnvironmentSection(appName, environment));
|
|
277
|
+
lines.push("");
|
|
278
|
+
}
|
|
279
|
+
return lines.join("\n").trimEnd() + "\n";
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const module$1 = defineNuxtModule({
|
|
283
|
+
meta: {
|
|
284
|
+
name: "@nuxtlib/cf-deployment",
|
|
285
|
+
configKey: "cfDeployment",
|
|
286
|
+
compatibility: {
|
|
287
|
+
nuxt: ">=3.0.0 <5.0.0"
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
// Default configuration options of the Nuxt module
|
|
291
|
+
defaults: {},
|
|
292
|
+
// This setup function is here to generate deployment config during Nuxt setup.
|
|
293
|
+
// It renders `wrangler.toml` from `cfDeployment` options and writes it to the project root.
|
|
294
|
+
// We need this so deploy settings stay in sync with app config every time setup runs.
|
|
295
|
+
async setup(options, nuxt) {
|
|
296
|
+
const logger = useLogger("@nuxtlib/cf-deployment");
|
|
297
|
+
const wranglerPath = resolve(nuxt.options.rootDir, "wrangler.toml");
|
|
298
|
+
const workflowPath = resolve(nuxt.options.rootDir, DEFAULT_WORKFLOW_PATH);
|
|
299
|
+
const wranglerContent = renderWranglerToml(options);
|
|
300
|
+
await writeFile(wranglerPath, wranglerContent, "utf8");
|
|
301
|
+
logger.success(`Generated wrangler.toml at ${wranglerPath}`);
|
|
302
|
+
const workflowContent = renderWorkflowYaml(options);
|
|
303
|
+
await mkdir(dirname(workflowPath), { recursive: true });
|
|
304
|
+
await writeFile(workflowPath, workflowContent, "utf8");
|
|
305
|
+
logger.success(`Generated workflow file at ${workflowPath}`);
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
export { module$1 as default };
|
package/dist/types.d.mts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nuxtlib/cf-deployment",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "A Nuxt module for Cloudflare Workers deployment with GitHub Actions integration.",
|
|
5
|
+
"repository": "https://github.com/nuxtlibv2/cf-deployment",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/types.d.mts",
|
|
14
|
+
"import": "./dist/module.mjs"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"main": "./dist/module.mjs",
|
|
18
|
+
"typesVersions": {
|
|
19
|
+
"*": {
|
|
20
|
+
".": [
|
|
21
|
+
"./dist/types.d.mts"
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"workspaces": [
|
|
29
|
+
"playground"
|
|
30
|
+
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"prepack": "nuxt-module-build build",
|
|
33
|
+
"dev": "npm run dev:prepare && nuxt dev playground",
|
|
34
|
+
"dev:build": "nuxt build playground",
|
|
35
|
+
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt prepare playground",
|
|
36
|
+
"release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish && git push --follow-tags",
|
|
37
|
+
"lint": "eslint .",
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"test:watch": "vitest watch",
|
|
40
|
+
"test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@nuxt/kit": "^4.3.1"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@nuxt/devtools": "^3.2.1",
|
|
47
|
+
"@nuxt/eslint-config": "^1.15.1",
|
|
48
|
+
"@nuxt/module-builder": "^1.0.2",
|
|
49
|
+
"@nuxt/schema": "^4.3.1",
|
|
50
|
+
"@nuxt/test-utils": "^4.0.0",
|
|
51
|
+
"@types/node": "latest",
|
|
52
|
+
"changelogen": "^0.6.2",
|
|
53
|
+
"eslint": "^10.0.0",
|
|
54
|
+
"nuxt": "^4.3.1",
|
|
55
|
+
"typescript": "~5.9.3",
|
|
56
|
+
"vitest": "^4.0.18",
|
|
57
|
+
"vue-tsc": "^3.2.4"
|
|
58
|
+
}
|
|
59
|
+
}
|