Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/slow-buses-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tanstack/create": patch
---

Add a Worker-safe edge export backed by a build-time generated template and add-on manifest.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ my-app
coverage
playwright-report
test-results

packages/create/src/generated/
39 changes: 39 additions & 0 deletions docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,45 @@ tanstack create --list-add-ons --framework React --json
tanstack create --addon-details drizzle --framework React --json
```

### Worker-safe programmatic generation

Use `@tanstack/create/edge` in Cloudflare Workers and other runtimes that do not expose a Node package filesystem. The default `@tanstack/create` export is still the Node/CLI path and scans framework templates from disk.

```ts
import {
createApp,
createMemoryEnvironment,
finalizeAddOns,
getFrameworkById,
populateAddOnOptionsDefaults,
} from '@tanstack/create/edge'

const framework = getFrameworkById('react')!
const chosenAddOns = await finalizeAddOns(framework, 'file-router', [
'tanstack-query',
'cloudflare',
])
const addOnOptions = populateAddOnOptionsDefaults(chosenAddOns)
const { environment, output } = createMemoryEnvironment('/app')

await createApp(environment, {
projectName: 'app',
targetDir: '/app',
framework,
mode: 'file-router',
typescript: true,
tailwind: true,
packageManager: 'pnpm',
git: false,
install: false,
intent: false,
chosenAddOns,
addOnOptions,
})

// output.files contains generated files for ZIP creation.
```

---

## tanstack add
Expand Down
1 change: 1 addition & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default [
ignores: [
'**/assets/**',
'**/dist/**',
'**/src/generated/**',
],
},
...tanstackConfig,
Expand Down
28 changes: 24 additions & 4 deletions packages/create/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,32 @@
"type": "module",
"main": "./dist/index.js",
"types": "./dist/types/index.d.ts",
"exports": {
".": {
"types": "./dist/types/index.d.ts",
"import": "./dist/index.js",
"default": "./dist/index.js"
},
"./edge": {
"types": "./dist/types/edge.d.ts",
"import": "./dist/edge.js",
"default": "./dist/edge.js"
},
"./manifest": {
"types": "./dist/types/manifest.d.ts",
"import": "./dist/manifest.js",
"default": "./dist/manifest.js"
},
"./dist/*": "./dist/*",
"./package.json": "./package.json"
},
"scripts": {
"build": "tsc && npm run copy-assets",
"build": "npm run generate-manifest && tsc && npm run copy-assets",
"generate-manifest": "node ./scripts/generate-manifest.mjs",
"copy-assets": "node -e \"const fs=require('fs');const path=require('path');function copyDir(src,dest){if(!fs.existsSync(dest))fs.mkdirSync(dest,{recursive:true});for(const entry of fs.readdirSync(src,{withFileTypes:true})){const srcPath=path.join(src,entry.name);const destPath=path.join(dest,entry.name);if(entry.isDirectory())copyDir(srcPath,destPath);else fs.copyFileSync(srcPath,destPath)}}['react','solid'].forEach(fw=>{['add-ons','toolchains','hosts','examples','project'].forEach(dir=>{const src='src/frameworks/'+fw+'/'+dir;const dest='dist/frameworks/'+fw+'/'+dir;if(fs.existsSync(src))copyDir(src,dest)})})\"",
"dev": "tsc --watch",
"test": "eslint ./src && vitest run",
"test:watch": "vitest",
"dev": "npm run generate-manifest && tsc --watch",
"test": "npm run generate-manifest && eslint ./src && vitest run",
"test:watch": "npm run generate-manifest && vitest",
Comment on lines 27 to +33

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

test:coverage should also run manifest generation in clean environments.

This block correctly adds codegen pre-steps for build/dev/test flows, but coverage still misses that prerequisite and can fail when src/generated/create-manifest.ts is absent.

Proposed fix
-    "test:coverage": "vitest run --coverage"
+    "test:coverage": "npm run generate-manifest && vitest run --coverage"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/create/package.json` around lines 27 - 33, The test:coverage script
is missing the manifest generation prerequisite that other scripts include. Add
"npm run generate-manifest &&" to the beginning of the test:coverage script
command to ensure the generated manifest file exists before running coverage
tests, following the same pattern used in the build, dev, test, and test:watch
scripts.

"test:coverage": "vitest run --coverage"
},
"repository": {
Expand Down
Loading
Loading