Skip to content

Commit 9a1de61

Browse files
authored
Support Tanstack in autoconfig (#11375)
* Create orange-tables-join.md * Support TanStack * Create happy-toys-develop.md * Add support for Tanstack in autoconfig Support Tanstack in autoconfig experimental flow. * fix test * fix lint * remove astro changeset * Build before preview * Check both TS and JS Vite config files
1 parent 5e937c1 commit 9a1de61

File tree

13 files changed

+371
-31
lines changed

13 files changed

+371
-31
lines changed

.changeset/happy-toys-devellop.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"create-cloudflare": patch
3+
---
4+
5+
Support Tanstack in autoconfig `--experimental` flow

.changeset/happy-toys-develop.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"wrangler": minor
3+
---
4+
5+
Support Tanstack in autoconfig

packages/create-cloudflare/e2e/helpers/framework-helpers.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,11 @@ export async function verifyPreviewScript(
256256
"Expected a preview script is we are verifying the preview in " +
257257
projectPath,
258258
);
259+
if (verifyPreview.build) {
260+
await runCommand([packageManager.name, "run", "build"], {
261+
cwd: projectPath,
262+
});
263+
}
259264

260265
// Run the dev-server on random ports to avoid colliding with other tests
261266
const port = await getPort();

packages/create-cloudflare/e2e/helpers/run-c3.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export type RunnerConfig = {
4141
previewArgs?: string[];
4242
route: string;
4343
expectedText: string;
44+
build?: boolean;
4445
};
4546
/**
4647
* Specifies whether to run the test script for the project and verify the exit code.

packages/create-cloudflare/e2e/tests/cli/cli.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ describe("Create Cloudflare CLI", () => {
555555
npm create cloudflare -- --framework next -- --ts
556556
pnpm create cloudflare --framework next -- --ts
557557
Allowed Values:
558-
gatsby, svelte, docusaurus, astro
558+
gatsby, svelte, docusaurus, astro, tanstack-start
559559
--platform=<value>
560560
Whether the application should be deployed to Pages or Workers. This is only applicable for Frameworks templates that support both Pages and Workers.
561561
Allowed Values:

packages/create-cloudflare/e2e/tests/frameworks/test-config.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,7 @@ function getFrameworkTestConfig(pm: string): NamedFrameworkTestConfig[] {
599599
verifyPreview: {
600600
route: "/",
601601
expectedText: "TanStack Start Starter",
602+
build: true,
602603
},
603604
nodeCompat: true,
604605
},
@@ -727,6 +728,23 @@ function getExperimentalFrameworkTestConfig(
727728
],
728729
verifyTypes: false,
729730
},
731+
{
732+
name: "tanstack-start",
733+
testCommitMessage: true,
734+
timeout: LONG_TIMEOUT,
735+
unsupportedOSs: ["win32"],
736+
verifyDeploy: {
737+
route: "/",
738+
expectedText: "TanStack Start Starter",
739+
},
740+
verifyPreview: {
741+
route: "/",
742+
expectedText: "TanStack Start Starter",
743+
build: true,
744+
},
745+
nodeCompat: true,
746+
verifyTypes: false,
747+
},
730748
];
731749
}
732750

packages/create-cloudflare/src/templates.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ export function getFrameworkMap({ experimental = false }): TemplateMap {
240240
svelte: svelteTemplate,
241241
docusaurus: docusaurusTemplate,
242242
astro: astroTemplate,
243+
"tanstack-start": tanStackStartTemplate,
243244
};
244245
} else {
245246
return {

packages/create-cloudflare/templates/tanstack-start/c3.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,6 @@ const config: TemplateConfig = {
3636
}),
3737
devScript: "dev",
3838
deployScript: "deploy",
39-
previewScript: "preview",
39+
previewScript: "serve",
4040
};
4141
export default config;

packages/wrangler/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
"@sentry/utils": "^7.86.0",
9595
"@types/command-exists": "^1.2.0",
9696
"@types/cross-spawn": "^6.0.2",
97+
"@types/esprima": "^4.0.3",
9798
"@types/glob-to-regexp": "^0.4.1",
9899
"@types/is-ci": "^3.0.0",
99100
"@types/javascript-time-ago": "^2.0.3",
@@ -125,6 +126,7 @@
125126
"dotenv": "^16.3.1",
126127
"dotenv-expand": "^12.0.2",
127128
"eslint": "catalog:default",
129+
"esprima": "4.0.1",
128130
"execa": "^6.1.0",
129131
"find-up": "^6.3.0",
130132
"get-port": "^7.0.0",
@@ -146,6 +148,7 @@
146148
"patch-console": "^1.0.0",
147149
"pretty-bytes": "^6.0.0",
148150
"prompts": "^2.4.2",
151+
"recast": "0.22.0",
149152
"resolve": "^1.22.8",
150153
"rimraf": "catalog:default",
151154
"semiver": "^1.1.0",
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import { existsSync, lstatSync, readdirSync, writeFileSync } from "node:fs";
2+
import path, { extname, join } from "node:path";
3+
import { readFileSync } from "@cloudflare/workers-utils";
4+
import * as recast from "recast";
5+
import * as esprimaParser from "recast/parsers/esprima";
6+
import * as typescriptParser from "recast/parsers/typescript";
7+
import type { Program } from "esprima";
8+
9+
/*
10+
CODEMOD TIPS & TRICKS
11+
=====================
12+
13+
More info about parsing and transforming can be found in the `recast` docs:
14+
https://github.com/benjamn/recast
15+
16+
`recast` uses the `ast-types` library under the hood for basic AST operations
17+
and defining node types. If you need to manipulate or manually construct AST nodes as
18+
part of a code mod operation, be sure to check the `ast-types` documentation:
19+
https://github.com/benjamn/ast-types
20+
21+
Last but not least, AST viewers can be extremely helpful when trying to write
22+
a transformer:
23+
- https://astexplorer.net/
24+
- https://ts-ast-viewer.com/#
25+
26+
*/
27+
28+
// Parse an input string as javascript and return an ast
29+
export const parseJs = (src: string) => {
30+
src = src.trim();
31+
try {
32+
return recast.parse(src, { parser: esprimaParser });
33+
} catch {
34+
throw new Error("Error parsing js template.");
35+
}
36+
};
37+
38+
// Parse an input string as typescript and return an ast
39+
export const parseTs = (src: string) => {
40+
src = src.trim();
41+
try {
42+
return recast.parse(src, { parser: typescriptParser });
43+
} catch {
44+
throw new Error("Error parsing ts template.");
45+
}
46+
};
47+
48+
// Parse a provided file with recast and return an ast
49+
// Selects the correct parser based on the file extension
50+
export const parseFile = (filePath: string) => {
51+
const lang = path.extname(filePath).slice(1);
52+
const parser = lang === "js" ? esprimaParser : typescriptParser;
53+
54+
try {
55+
const fileContents = readFileSync(path.resolve(filePath));
56+
57+
if (fileContents) {
58+
return recast.parse(fileContents, { parser }).program as Program;
59+
}
60+
} catch {
61+
throw new Error(`Error parsing file: ${filePath}`);
62+
}
63+
64+
return null;
65+
};
66+
67+
// Transform a file with the provided transformer methods and write it back to disk
68+
export const transformFile = (
69+
filePath: string,
70+
methods: recast.types.Visitor
71+
) => {
72+
const ast = parseFile(filePath);
73+
74+
if (ast) {
75+
recast.visit(ast, methods);
76+
writeFileSync(filePath, recast.print(ast).code);
77+
}
78+
};
79+
80+
export const loadSnippets = (parentFolder: string) => {
81+
const snippetsPath = join(parentFolder, "snippets");
82+
83+
if (!existsSync(snippetsPath)) {
84+
return {};
85+
}
86+
87+
if (!lstatSync(snippetsPath).isDirectory) {
88+
return {};
89+
}
90+
91+
const files = readdirSync(snippetsPath);
92+
93+
return (
94+
files
95+
// don't try loading directories
96+
.filter((fileName) => lstatSync(join(snippetsPath, fileName)).isFile)
97+
// only load js or ts files
98+
.filter((fileName) => [".js", ".ts"].includes(extname(fileName)))
99+
.reduce((acc, snippetPath) => {
100+
const [file, ext] = snippetPath.split(".");
101+
const key = `${file}${ext === "js" ? "Js" : "Ts"}`;
102+
return {
103+
...acc,
104+
[key]: parseFile(join(snippetsPath, snippetPath))?.body,
105+
};
106+
}, {}) as Record<string, recast.types.ASTNode[]>
107+
);
108+
};
109+
110+
/**
111+
* merges provided properties into a given object (updating the object itself), deeply merging them in case
112+
* some properties are object themselves
113+
*
114+
* @param sourceObject the object into which merge the new properties
115+
* @param newProperties the new properties to add/merge
116+
*/
117+
export const mergeObjectProperties = (
118+
sourceObject: recast.types.namedTypes.ObjectExpression,
119+
newProperties: recast.types.namedTypes.ObjectProperty[]
120+
): void => {
121+
newProperties.forEach((newProp) => {
122+
const newPropName = getPropertyName(newProp);
123+
if (!newPropName) {
124+
return false;
125+
}
126+
const indexOfExisting = sourceObject.properties.findIndex(
127+
(p) => p.type === "ObjectProperty" && getPropertyName(p) === newPropName
128+
);
129+
130+
const existing = sourceObject.properties[indexOfExisting];
131+
if (!existing) {
132+
sourceObject.properties.push(newProp);
133+
return;
134+
}
135+
136+
if (
137+
existing.type === "ObjectProperty" &&
138+
existing.value.type === "ObjectExpression" &&
139+
newProp.value.type === "ObjectExpression"
140+
) {
141+
mergeObjectProperties(
142+
existing.value,
143+
newProp.value.properties as recast.types.namedTypes.ObjectProperty[]
144+
);
145+
return;
146+
}
147+
148+
sourceObject.properties[indexOfExisting] = newProp;
149+
});
150+
};
151+
152+
const getPropertyName = (newProp: recast.types.namedTypes.ObjectProperty) => {
153+
return newProp.key.type === "Identifier"
154+
? newProp.key.name
155+
: newProp.key.type === "StringLiteral"
156+
? newProp.key.value
157+
: null;
158+
};

0 commit comments

Comments
 (0)