create-newt-app 0.32.1 → 0.33.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/dist/index.js CHANGED
@@ -11,7 +11,7 @@ import { existsSync, promises } from "fs";
11
11
  import path from "path";
12
12
  import https from "node:https";
13
13
  //#region package.json
14
- var version = "0.32.1";
14
+ var version = "0.33.1";
15
15
  var engines = { "node": ">=24.0.0" };
16
16
  //#endregion
17
17
  //#region src/templates/root/index.ts
@@ -231,7 +231,7 @@ This version has breaking changes — APIs, conventions, and file structure may
231
231
  const web$1 = {
232
232
  templates: [
233
233
  {
234
- when: (s) => s.nest !== "di-only",
234
+ when: (s) => s.nest !== "di-only" && !s.stylex,
235
235
  filename: "apps/web/package.json",
236
236
  template: `{
237
237
  "name": "web",
@@ -369,6 +369,7 @@ const nextConfig = {
369
369
  export default nextConfig;`
370
370
  },
371
371
  {
372
+ when: (s) => !s.stylex,
372
373
  filename: "apps/web/postcss.config.mjs",
373
374
  template: `export { default } from "@<%= projectName %>/ui/postcss.config";`
374
375
  },
@@ -399,7 +400,7 @@ export default nextConfig;`
399
400
  }`
400
401
  },
401
402
  {
402
- when: (s) => !s.shadcn,
403
+ when: (s) => !s.shadcn && !s.stylex,
403
404
  filename: "apps/web/app/layout.tsx",
404
405
  template: `import type { Metadata } from "next";
405
406
  import localFont from "next/font/local";
@@ -442,7 +443,7 @@ export default function RootLayout({
442
443
  }`
443
444
  },
444
445
  {
445
- when: (s) => !s.todoExample && !s.shadcn,
446
+ when: (s) => !s.todoExample && !s.shadcn && !s.stylex,
446
447
  filename: "apps/web/app/page.tsx",
447
448
  template: `'use client';
448
449
 
@@ -543,7 +544,7 @@ export default function Providers({ children }: { children: React.ReactNode }) {
543
544
  }`
544
545
  },
545
546
  {
546
- when: (s) => !s.shadcn,
547
+ when: (s) => !s.shadcn && !s.stylex,
547
548
  filename: "apps/web/app/auth-form.tsx",
548
549
  template: `'use client';
549
550
 
@@ -1142,6 +1143,7 @@ var package_json_sqlite_default = {
1142
1143
  "name": "@<%= projectName %>/db",
1143
1144
  "version": "0.0.0",
1144
1145
  "private": true,
1146
+ "type": "module",
1145
1147
  "exports": {
1146
1148
  ".": "./src/index.ts"
1147
1149
  },
@@ -1171,6 +1173,7 @@ var package_json_postgres_default = {
1171
1173
  "name": "@<%= projectName %>/db",
1172
1174
  "version": "0.0.0",
1173
1175
  "private": true,
1176
+ "type": "module",
1174
1177
  "exports": {
1175
1178
  ".": "./src/index.ts"
1176
1179
  },
@@ -1258,11 +1261,10 @@ const dbPostgres = { templates: [
1258
1261
  src_index_postgres_default
1259
1262
  ] };
1260
1263
  //#endregion
1261
- //#region src/templates/ui/index.ts
1262
- const ui$1 = { templates: [
1263
- {
1264
- filename: "packages/ui/package.json",
1265
- template: `{
1264
+ //#region src/templates/ui/templates/package-json.ts
1265
+ var package_json_default$4 = {
1266
+ filename: "packages/ui/package.json",
1267
+ template: `{
1266
1268
  "name": "@<%= projectName %>/ui",
1267
1269
  "version": "0.0.0",
1268
1270
  "private": true,
@@ -1294,10 +1296,12 @@ const ui$1 = { templates: [
1294
1296
  "tailwind-merge": "<%= versions["tailwind-merge"] %>"
1295
1297
  }
1296
1298
  }`
1297
- },
1298
- {
1299
- filename: "packages/ui/src/button.tsx",
1300
- template: `"use client";
1299
+ };
1300
+ //#endregion
1301
+ //#region src/templates/ui/templates/button.ts
1302
+ var button_default$2 = {
1303
+ filename: "packages/ui/src/button.tsx",
1304
+ template: `"use client";
1301
1305
 
1302
1306
  import { ReactNode } from "react";
1303
1307
  import { cn } from "./utils";
@@ -1329,10 +1333,12 @@ export const Button = ({ children, onClick, type = "button", disabled, className
1329
1333
  </button>
1330
1334
  );
1331
1335
  };`
1332
- },
1333
- {
1334
- filename: "packages/ui/src/link.tsx",
1335
- template: `import NextLink, { type LinkProps } from "next/link";
1336
+ };
1337
+ //#endregion
1338
+ //#region src/templates/ui/templates/link.ts
1339
+ var link_default$2 = {
1340
+ filename: "packages/ui/src/link.tsx",
1341
+ template: `import NextLink, { type LinkProps } from "next/link";
1336
1342
  import { type PropsWithChildren } from "react";
1337
1343
  import { cn } from "./utils";
1338
1344
 
@@ -1355,10 +1361,12 @@ export function Link({
1355
1361
  </NextLink>
1356
1362
  );
1357
1363
  }`
1358
- },
1359
- {
1360
- filename: "packages/ui/src/logo.tsx",
1361
- template: `import { cn } from "./utils";
1364
+ };
1365
+ //#endregion
1366
+ //#region src/templates/ui/templates/logo.ts
1367
+ var logo_default$2 = {
1368
+ filename: "packages/ui/src/logo.tsx",
1369
+ template: `import { cn } from "./utils";
1362
1370
 
1363
1371
  interface LogoProps {
1364
1372
  className?: string;
@@ -1381,19 +1389,23 @@ export function Logo({ className }: LogoProps) {
1381
1389
  </svg>
1382
1390
  );
1383
1391
  }`
1384
- },
1385
- {
1386
- filename: "packages/ui/src/utils.ts",
1387
- template: `import { clsx, type ClassValue } from "clsx";
1392
+ };
1393
+ //#endregion
1394
+ //#region src/templates/ui/templates/utils.ts
1395
+ var utils_default$1 = {
1396
+ filename: "packages/ui/src/utils.ts",
1397
+ template: `import { clsx, type ClassValue } from "clsx";
1388
1398
  import { twMerge } from "tailwind-merge";
1389
1399
 
1390
1400
  export function cn(...inputs: ClassValue[]) {
1391
1401
  return twMerge(clsx(inputs));
1392
1402
  }`
1393
- },
1394
- {
1395
- filename: "packages/ui/src/globals.css",
1396
- template: `@import 'tailwindcss';
1403
+ };
1404
+ //#endregion
1405
+ //#region src/templates/ui/templates/globals-css.ts
1406
+ var globals_css_default$2 = {
1407
+ filename: "packages/ui/src/globals.css",
1408
+ template: `@import 'tailwindcss';
1397
1409
  @source "../../../apps/**/*.{ts,tsx}";
1398
1410
  @source "../**/*.{ts,tsx}";
1399
1411
 
@@ -1430,20 +1442,24 @@ export function cn(...inputs: ClassValue[]) {
1430
1442
  border-color: var(--border);
1431
1443
  }
1432
1444
  }`
1433
- },
1434
- {
1435
- filename: "packages/ui/postcss.config.mjs",
1436
- template: `const config = {
1445
+ };
1446
+ //#endregion
1447
+ //#region src/templates/ui/templates/postcss-config.ts
1448
+ var postcss_config_default$2 = {
1449
+ filename: "packages/ui/postcss.config.mjs",
1450
+ template: `const config = {
1437
1451
  plugins: {
1438
1452
  "@tailwindcss/postcss": {},
1439
1453
  },
1440
1454
  };
1441
1455
 
1442
1456
  export default config;`
1443
- },
1444
- {
1445
- filename: "packages/ui/tsconfig.json",
1446
- template: `{
1457
+ };
1458
+ //#endregion
1459
+ //#region src/templates/ui/templates/tsconfig.ts
1460
+ var tsconfig_default$1 = {
1461
+ filename: "packages/ui/tsconfig.json",
1462
+ template: `{
1447
1463
  "extends": "@<%= projectName %>/typescript-config/react-library.json",
1448
1464
  "compilerOptions": {
1449
1465
  "outDir": "dist",
@@ -1452,11 +1468,24 @@ export default config;`
1452
1468
  "include": ["src"],
1453
1469
  "exclude": ["node_modules", "dist"]
1454
1470
  }`
1455
- }
1471
+ };
1472
+ //#endregion
1473
+ //#region src/templates/ui/index.ts
1474
+ const ui$1 = { templates: [
1475
+ package_json_default$4,
1476
+ button_default$2,
1477
+ link_default$2,
1478
+ logo_default$2,
1479
+ utils_default$1,
1480
+ globals_css_default$2,
1481
+ postcss_config_default$2,
1482
+ tsconfig_default$1
1456
1483
  ] };
1457
1484
  //#endregion
1458
1485
  //#region src/templates/versions.ts
1459
1486
  const versions = {
1487
+ "@babel/plugin-proposal-decorators": "^8.0.2",
1488
+ "@babel/plugin-transform-class-properties": "^8.0.1",
1460
1489
  "@base-ui/react": "^1.6.0",
1461
1490
  "@eslint/js": "^9.39.1",
1462
1491
  "@nestjs/cli": "^11.0.0",
@@ -1469,6 +1498,9 @@ const versions = {
1469
1498
  "@next/eslint-plugin-next": "^16.3.4",
1470
1499
  "@oxlint/plugins": "^1.74.0",
1471
1500
  "@shadcn/react": "^0.2.1",
1501
+ "@stylexjs/babel-plugin": "0.19.0",
1502
+ "@stylexjs/postcss-plugin": "0.19.0",
1503
+ "@stylexjs/stylex": "0.19.0",
1472
1504
  "@swc/core": "^1.10.0",
1473
1505
  "@tailwindcss/postcss": "^4.2.1",
1474
1506
  "@tanstack/react-form": "^1.28.5",
@@ -1484,6 +1516,7 @@ const versions = {
1484
1516
  "@types/supertest": "^6.0.2",
1485
1517
  "@vitest/coverage-v8": "^4.1.10",
1486
1518
  auth: "^1.5.5",
1519
+ "babel-plugin-transform-typescript-metadata": "^0.4.0",
1487
1520
  "better-auth": "^1.2.8",
1488
1521
  "better-sqlite3": "^12.11.1",
1489
1522
  "class-variance-authority": "^0.7.1",
@@ -9829,152 +9862,951 @@ export {
9829
9862
  }]
9830
9863
  };
9831
9864
  //#endregion
9832
- //#region src/templates/eslint-config/templates/package-json.ts
9833
- var package_json_default$1 = {
9834
- filename: "packages/eslint-config/package.json",
9835
- template: `{
9836
- "name": "@<%= projectName %>/eslint-config",
9865
+ //#region src/templates/stylex-ui/index.ts
9866
+ const stylexUi = { templates: [
9867
+ {
9868
+ filename: "packages/ui/package.json",
9869
+ template: `{
9870
+ "name": "@<%= projectName %>/ui",
9837
9871
  "version": "0.0.0",
9838
- "type": "module",
9839
9872
  "private": true,
9840
9873
  "exports": {
9841
- "./base": "./base.js",
9842
- "./next-js": "./next.js",
9843
- "./react-internal": "./react-internal.js"
9874
+ "./globals.css": "./src/globals.css",
9875
+ "./tokens.stylex": "./src/tokens.stylex.ts",
9876
+ "./*": "./src/*.tsx"
9877
+ },
9878
+ "scripts": {
9879
+ "typecheck": "tsc --noEmit"
9844
9880
  },
9845
9881
  "devDependencies": {
9846
- "@eslint/js": "<%= versions["@eslint/js"] %>",
9847
- "@next/eslint-plugin-next": "<%= versions["@next/eslint-plugin-next"] %>",
9848
- "eslint": "<%= versions.eslint %>",
9849
- "eslint-config-prettier": "<%= versions["eslint-config-prettier"] %>",
9850
- "eslint-plugin-only-warn": "<%= versions["eslint-plugin-only-warn"] %>",
9851
- "eslint-plugin-react": "<%= versions["eslint-plugin-react"] %>",
9852
- "eslint-plugin-react-hooks": "<%= versions["eslint-plugin-react-hooks"] %>",
9853
- "eslint-plugin-turbo": "<%= versions["eslint-plugin-turbo"] %>",
9854
- "globals": "<%= versions.globals %>",
9855
- "typescript": "<%= versions.typescript %>",
9856
- "typescript-eslint": "<%= versions["typescript-eslint"] %>"
9882
+ "@<%= projectName %>/typescript-config": "workspace:*",
9883
+ "@types/node": "<%= versions["@types/node"] %>",
9884
+ "@types/react": "<%= versions["@types/react"] %>",
9885
+ "@types/react-dom": "<%= versions["@types/react-dom"] %>",
9886
+ "typescript": "<%= versions.typescript %>"
9887
+ },
9888
+ "peerDependencies": {
9889
+ "next": ">=15.0.0",
9890
+ "react": ">=19.0.0",
9891
+ "react-dom": ">=19.0.0"
9892
+ },
9893
+ "dependencies": {
9894
+ "@stylexjs/stylex": "<%= versions["@stylexjs/stylex"] %>"
9857
9895
  }
9858
9896
  }`
9897
+ },
9898
+ {
9899
+ filename: "packages/ui/src/tokens.stylex.ts",
9900
+ template: `import * as stylex from "@stylexjs/stylex";
9901
+
9902
+ export const colors = stylex.defineVars({
9903
+ background: "oklch(0.145 0 0)",
9904
+ foreground: "oklch(0.985 0 0)",
9905
+ muted: "oklch(0.269 0 0)",
9906
+ mutedForeground: "oklch(0.708 0 0)",
9907
+ border: "oklch(0.469 0 0)",
9908
+ accent: "oklch(0.985 0 0)",
9909
+ accentForeground: "oklch(0.205 0 0)",
9910
+ danger: "oklch(0.637 0.237 25.331)",
9911
+ });
9912
+
9913
+ export const fonts = stylex.defineVars({
9914
+ sans: "var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif",
9915
+ mono: "var(--font-geist-mono), ui-monospace, monospace",
9916
+ });
9917
+
9918
+ export const radii = stylex.defineVars({
9919
+ sm: "6px",
9920
+ md: "8px",
9921
+ lg: "12px",
9922
+ });
9923
+ `
9924
+ },
9925
+ {
9926
+ filename: "packages/ui/src/button.tsx",
9927
+ template: `"use client";
9928
+
9929
+ import { ReactNode } from "react";
9930
+ import * as stylex from "@stylexjs/stylex";
9931
+ import type { StyleXStyles } from "@stylexjs/stylex";
9932
+ import { colors, fonts, radii } from "./tokens.stylex";
9933
+
9934
+ interface ButtonProps {
9935
+ children: ReactNode;
9936
+ onClick?: () => void;
9937
+ type?: "button" | "submit" | "reset";
9938
+ disabled?: boolean;
9939
+ style?: StyleXStyles;
9940
+ }
9941
+
9942
+ export const Button = ({ children, onClick, type = "button", disabled, style }: ButtonProps) => {
9943
+ return (
9944
+ <button
9945
+ type={type}
9946
+ onClick={onClick}
9947
+ disabled={disabled}
9948
+ {...stylex.props(styles.base, disabled && styles.disabled, style)}
9949
+ >
9950
+ {children}
9951
+ </button>
9952
+ );
9859
9953
  };
9860
- //#endregion
9861
- //#region src/templates/eslint-config/templates/base.ts
9862
- var base_default$1 = {
9863
- filename: "packages/eslint-config/base.js",
9864
- template: `import js from "@eslint/js";
9865
- import eslintConfigPrettier from "eslint-config-prettier";
9866
- import turboPlugin from "eslint-plugin-turbo";
9867
- import tseslint from "typescript-eslint";
9868
- import onlyWarn from "eslint-plugin-only-warn";
9869
9954
 
9870
- /**
9871
- * A shared ESLint configuration for the repository.
9872
- *
9873
- * @type {import("eslint").Linter.Config[]}
9874
- * */
9875
- export const config = [
9876
- js.configs.recommended,
9877
- eslintConfigPrettier,
9878
- ...tseslint.configs.recommended,
9879
- {
9880
- plugins: {
9881
- turbo: turboPlugin,
9882
- },
9883
- rules: {
9884
- "turbo/no-undeclared-env-vars": "warn",
9955
+ const styles = stylex.create({
9956
+ base: {
9957
+ display: "inline-flex",
9958
+ alignItems: "center",
9959
+ justifyContent: "center",
9960
+ gap: 8,
9961
+ height: 36,
9962
+ paddingBlock: 0,
9963
+ paddingInline: 16,
9964
+ fontFamily: fonts.sans,
9965
+ fontSize: 14,
9966
+ fontWeight: 500,
9967
+ whiteSpace: "nowrap",
9968
+ color: colors.accentForeground,
9969
+ backgroundColor: {
9970
+ default: colors.accent,
9971
+ ":hover": "oklch(0.922 0 0)",
9885
9972
  },
9886
- },
9887
- {
9888
- plugins: {
9889
- onlyWarn,
9973
+ borderStyle: "none",
9974
+ borderRadius: radii.sm,
9975
+ boxShadow: "0 1px 2px rgba(0, 0, 0, 0.3)",
9976
+ cursor: "pointer",
9977
+ transitionProperty: "background-color",
9978
+ transitionDuration: "150ms",
9979
+ outlineColor: "rgba(255, 255, 255, 0.5)",
9980
+ outlineOffset: 2,
9981
+ outlineStyle: {
9982
+ default: "none",
9983
+ ":focus-visible": "solid",
9890
9984
  },
9985
+ outlineWidth: 2,
9891
9986
  },
9892
- {
9893
- ignores: ["dist/**"],
9987
+ disabled: {
9988
+ opacity: 0.5,
9989
+ pointerEvents: "none",
9894
9990
  },
9895
- ];`
9896
- };
9897
- //#endregion
9898
- //#region src/templates/eslint-config/templates/next.ts
9899
- var next_default = {
9900
- filename: "packages/eslint-config/next.js",
9901
- template: `import js from "@eslint/js";
9902
- import { globalIgnores } from "eslint/config";
9903
- import eslintConfigPrettier from "eslint-config-prettier";
9904
- import tseslint from "typescript-eslint";
9905
- import pluginReactHooks from "eslint-plugin-react-hooks";
9906
- import pluginReact from "eslint-plugin-react";
9907
- import globals from "globals";
9908
- import pluginNext from "@next/eslint-plugin-next";
9909
- import { config as baseConfig } from "./base.js";
9991
+ });
9992
+ `
9993
+ },
9994
+ {
9995
+ filename: "packages/ui/src/link.tsx",
9996
+ template: `import NextLink, { type LinkProps } from "next/link";
9997
+ import { type PropsWithChildren } from "react";
9998
+ import * as stylex from "@stylexjs/stylex";
9999
+ import type { StyleXStyles } from "@stylexjs/stylex";
10000
+ import { colors } from "./tokens.stylex";
9910
10001
 
9911
- /**
9912
- * A custom ESLint configuration for libraries that use Next.js.
9913
- *
9914
- * @type {import("eslint").Linter.Config[]}
9915
- * */
9916
- export const nextJsConfig = [
9917
- ...baseConfig,
9918
- js.configs.recommended,
9919
- eslintConfigPrettier,
9920
- ...tseslint.configs.recommended,
9921
- globalIgnores([
9922
- // Default ignores of eslint-config-next:
9923
- ".next/**",
9924
- "out/**",
9925
- "build/**",
9926
- "next-env.d.ts",
9927
- ]),
9928
- {
9929
- ...pluginReact.configs.flat.recommended,
9930
- languageOptions: {
9931
- ...pluginReact.configs.flat.recommended.languageOptions,
9932
- globals: {
9933
- ...globals.serviceworker,
9934
- ...globals.node,
9935
- },
9936
- },
9937
- },
9938
- {
9939
- plugins: {
9940
- "@next/next": pluginNext,
9941
- },
9942
- rules: {
9943
- ...pluginNext.configs.recommended.rules,
9944
- ...pluginNext.configs["core-web-vitals"].rules,
9945
- },
9946
- },
9947
- {
9948
- plugins: {
9949
- "react-hooks": pluginReactHooks,
9950
- },
9951
- settings: { react: { version: "detect" } },
9952
- rules: {
9953
- ...pluginReactHooks.configs.recommended.rules,
9954
- // React scope no longer necessary with new JSX transform.
9955
- "react/react-in-jsx-scope": "off",
10002
+ export function Link({
10003
+ href,
10004
+ children,
10005
+ style,
10006
+ ...rest
10007
+ }: PropsWithChildren<LinkProps & { style?: StyleXStyles }>) {
10008
+ return (
10009
+ <NextLink href={href} {...rest} {...stylex.props(styles.link, style)}>
10010
+ {children}
10011
+ </NextLink>
10012
+ );
10013
+ }
10014
+
10015
+ const styles = stylex.create({
10016
+ link: {
10017
+ color: "inherit",
10018
+ textDecorationLine: "underline",
10019
+ textDecorationColor: {
10020
+ default: colors.mutedForeground,
10021
+ ":hover": colors.foreground,
9956
10022
  },
10023
+ transitionProperty: "text-decoration-color",
10024
+ transitionDuration: "150ms",
9957
10025
  },
9958
- ];`
9959
- };
9960
- //#endregion
9961
- //#region src/templates/eslint-config/templates/react-internal.ts
9962
- var react_internal_default = {
9963
- filename: "packages/eslint-config/react-internal.js",
9964
- template: `import js from "@eslint/js";
9965
- import eslintConfigPrettier from "eslint-config-prettier";
9966
- import tseslint from "typescript-eslint";
9967
- import pluginReactHooks from "eslint-plugin-react-hooks";
9968
- import pluginReact from "eslint-plugin-react";
9969
- import globals from "globals";
9970
- import { config as baseConfig } from "./base.js";
10026
+ });
10027
+ `
10028
+ },
10029
+ {
10030
+ filename: "packages/ui/src/logo.tsx",
10031
+ template: `import * as stylex from "@stylexjs/stylex";
10032
+ import type { StyleXStyles } from "@stylexjs/stylex";
10033
+ import { colors } from "./tokens.stylex";
9971
10034
 
9972
- /**
9973
- * A custom ESLint configuration for libraries that use React.
9974
- *
9975
- * @type {import("eslint").Linter.Config[]} */
9976
- export const config = [
9977
- ...baseConfig,
10035
+ interface LogoProps {
10036
+ style?: StyleXStyles;
10037
+ }
10038
+
10039
+ export function Logo({ style }: LogoProps) {
10040
+ return (
10041
+ <svg
10042
+ width="196"
10043
+ height="138"
10044
+ viewBox="0 0 196 138"
10045
+ fill="none"
10046
+ xmlns="http://www.w3.org/2000/svg"
10047
+ {...stylex.props(styles.logo, style)}
10048
+ >
10049
+ <path
10050
+ d="M98 35C102.665 35 107.243 35.2232 111.707 35.6514C111.243 38.1972 111 40.8202 111 43.5C111 67.5244 130.476 87 154.5 87C165.156 87 174.914 83.1658 182.478 76.8057C183.476 79.9455 184 83.186 184 86.5C184 114.943 145.496 138 98 138C50.5035 138 12 114.943 12 86.5C12 83.1861 12.5228 79.9454 13.5215 76.8057C21.0851 83.1661 30.8441 87 41.5 87C65.5244 87 85 67.5244 85 43.5C85 40.8202 84.7559 38.1973 84.292 35.6514C88.7559 35.2232 93.3345 35 98 35ZM38.5 0C59.763 0 77 17.237 77 38.5C77 59.763 59.763 77 38.5 77C17.237 77 0 59.763 0 38.5C0 17.237 17.237 0 38.5 0ZM157.5 0C178.763 0 196 17.237 196 38.5C196 59.763 178.763 77 157.5 77C136.237 77 119 59.763 119 38.5C119 17.237 136.237 0 157.5 0Z"
10051
+ fill="currentColor"
10052
+ />
10053
+ </svg>
10054
+ );
10055
+ }
10056
+
10057
+ const styles = stylex.create({
10058
+ logo: {
10059
+ color: colors.foreground,
10060
+ },
10061
+ });
10062
+ `
10063
+ },
10064
+ {
10065
+ filename: "packages/ui/src/globals.css",
10066
+ template: `@layer resets {
10067
+ *,
10068
+ *::before,
10069
+ *::after {
10070
+ box-sizing: border-box;
10071
+ }
10072
+
10073
+ * {
10074
+ margin: 0;
10075
+ padding: 0;
10076
+ }
10077
+
10078
+ html,
10079
+ body {
10080
+ height: 100%;
10081
+ }
10082
+
10083
+ html {
10084
+ -webkit-font-smoothing: antialiased;
10085
+ -moz-osx-font-smoothing: grayscale;
10086
+ }
10087
+ }
10088
+
10089
+ @stylex;
10090
+ `
10091
+ },
10092
+ tsconfig_default$1,
10093
+ {
10094
+ when: (s) => s.nest !== "di-only",
10095
+ filename: "apps/web/package.json",
10096
+ template: `{
10097
+ "name": "web",
10098
+ "version": "0.1.0",
10099
+ "type": "module",
10100
+ "private": true,
10101
+ "scripts": {
10102
+ "dev": "next dev --port 3000",
10103
+ "build": "next build",
10104
+ "start": "next start",
10105
+ "typecheck": "next typegen && tsc --noEmit"
10106
+ },
10107
+ "dependencies": {
10108
+ "@<%= projectName %>/auth": "workspace:*",
10109
+ "@<%= projectName %>/ui": "workspace:*",
10110
+ "@stylexjs/stylex": "<%= versions["@stylexjs/stylex"] %>",
10111
+ "dotenv": "<%= versions.dotenv %>",
10112
+ "@tanstack/react-form": "<%= versions["@tanstack/react-form"] %>",
10113
+ "@tanstack/react-query": "<%= versions["@tanstack/react-query"] %>",
10114
+ "better-auth": "<%= versions["better-auth"] %>",
10115
+ "next": "<%= versions.next %>",
10116
+ "react": "<%= versions.react %>",
10117
+ "react-dom": "<%= versions["react-dom"] %>"
10118
+ },
10119
+ "devDependencies": {
10120
+ "@<%= projectName %>/typescript-config": "workspace:*",
10121
+ "@stylexjs/babel-plugin": "<%= versions["@stylexjs/babel-plugin"] %>",
10122
+ "@stylexjs/postcss-plugin": "<%= versions["@stylexjs/postcss-plugin"] %>",
10123
+ "@types/node": "<%= versions["@types/node"] %>",
10124
+ "@types/react": "<%= versions["@types/react"] %>",
10125
+ "@types/react-dom": "<%= versions["@types/react-dom"] %>",
10126
+ "typescript": "<%= versions.typescript %>"
10127
+ }
10128
+ }`
10129
+ },
10130
+ {
10131
+ when: (s) => s.nest === "di-only",
10132
+ filename: "apps/web/package.json",
10133
+ template: `{
10134
+ "name": "web",
10135
+ "version": "0.1.0",
10136
+ "type": "module",
10137
+ "private": true,
10138
+ "scripts": {
10139
+ "dev": "next dev --port 3000",
10140
+ "build": "next build",
10141
+ "start": "next start"
10142
+ },
10143
+ "dependencies": {
10144
+ "@<%= projectName %>/api": "workspace:*",
10145
+ "@<%= projectName %>/auth": "workspace:*",
10146
+ "@<%= projectName %>/db": "workspace:*",
10147
+ "@<%= projectName %>/ui": "workspace:*",
10148
+ "@nestjs/common": "<%= versions["@nestjs/common"] %>",
10149
+ "@nestjs/core": "<%= versions["@nestjs/core"] %>",
10150
+ "reflect-metadata": "<%= versions["reflect-metadata"] %>",
10151
+ "@stylexjs/stylex": "<%= versions["@stylexjs/stylex"] %>",
10152
+ "dotenv": "<%= versions.dotenv %>",
10153
+ "@tanstack/react-form": "<%= versions["@tanstack/react-form"] %>",
10154
+ "@tanstack/react-query": "<%= versions["@tanstack/react-query"] %>",
10155
+ "better-auth": "<%= versions["better-auth"] %>",
10156
+ "next": "<%= versions.next %>",
10157
+ "react": "<%= versions.react %>",
10158
+ "react-dom": "<%= versions["react-dom"] %>"
10159
+ },
10160
+ "devDependencies": {
10161
+ "@<%= projectName %>/typescript-config": "workspace:*",
10162
+ "@babel/plugin-proposal-decorators": "<%= versions["@babel/plugin-proposal-decorators"] %>",
10163
+ "@babel/plugin-transform-class-properties": "<%= versions["@babel/plugin-transform-class-properties"] %>",
10164
+ "babel-plugin-transform-typescript-metadata": "<%= versions["babel-plugin-transform-typescript-metadata"] %>",
10165
+ "@stylexjs/babel-plugin": "<%= versions["@stylexjs/babel-plugin"] %>",
10166
+ "@stylexjs/postcss-plugin": "<%= versions["@stylexjs/postcss-plugin"] %>",
10167
+ "@types/node": "<%= versions["@types/node"] %>",
10168
+ "@types/react": "<%= versions["@types/react"] %>",
10169
+ "@types/react-dom": "<%= versions["@types/react-dom"] %>",
10170
+ "typescript": "<%= versions.typescript %>"
10171
+ }
10172
+ }`
10173
+ },
10174
+ {
10175
+ filename: "apps/web/babel.config.js",
10176
+ template: `import path from "node:path";
10177
+ import { fileURLToPath } from "node:url";
10178
+
10179
+ const dirname = path.dirname(fileURLToPath(import.meta.url));
10180
+
10181
+ const config = {
10182
+ presets: ["next/babel"],
10183
+ plugins: [
10184
+ <% if (nest === 'di-only') { %> "babel-plugin-transform-typescript-metadata",
10185
+ ["@babel/plugin-proposal-decorators", { version: "legacy" }],
10186
+ ["@babel/plugin-transform-class-properties", { loose: true }],
10187
+ <% } %> [
10188
+ "@stylexjs/babel-plugin",
10189
+ {
10190
+ dev: process.env.NODE_ENV !== "production",
10191
+ runtimeInjection: false,
10192
+ treeshakeCompensation: true,
10193
+ unstable_moduleResolution: {
10194
+ type: "commonJS",
10195
+ rootDir: path.join(dirname, "../.."),
10196
+ },
10197
+ },
10198
+ ],
10199
+ ],
10200
+ };
10201
+
10202
+ export default config;`
10203
+ },
10204
+ {
10205
+ filename: "apps/web/postcss.config.mjs",
10206
+ template: `import babelConfig from "./babel.config.js";
10207
+
10208
+ const config = {
10209
+ plugins: {
10210
+ "@stylexjs/postcss-plugin": {
10211
+ include: [
10212
+ "app/**/*.{js,jsx,ts,tsx}",
10213
+ "../../packages/ui/src/**/*.{js,jsx,ts,tsx}",
10214
+ ],
10215
+ babelConfig: {
10216
+ babelrc: false,
10217
+ parserOpts: { plugins: ["typescript", "jsx"] },
10218
+ plugins: babelConfig.plugins,
10219
+ },
10220
+ useCSSLayers: true,
10221
+ },
10222
+ },
10223
+ };
10224
+
10225
+ export default config;`
10226
+ },
10227
+ {
10228
+ filename: "apps/web/app/layout.tsx",
10229
+ template: `import type { Metadata } from "next";
10230
+ import localFont from "next/font/local";
10231
+ import * as stylex from "@stylexjs/stylex";
10232
+ import Providers from "@/app/providers";
10233
+ import { colors, fonts } from "@<%= projectName %>/ui/tokens.stylex";
10234
+ import "@<%= projectName %>/ui/globals.css";
10235
+
10236
+ const geistSans = localFont({
10237
+ src: "./fonts/GeistVF.woff",
10238
+ variable: "--font-geist-sans",
10239
+ });
10240
+ const geistMono = localFont({
10241
+ src: "./fonts/GeistMonoVF.woff",
10242
+ variable: "--font-geist-mono",
10243
+ });
10244
+
10245
+ export const metadata: Metadata = {
10246
+ title: "<%= projectName %>",
10247
+ description: "Next + Nest = Newt",
10248
+ icons: {
10249
+ icon: [
10250
+ { url: "/icon0.svg", type: "image/svg+xml" },
10251
+ { url: "/icon1.png", type: "image/png" },
10252
+ ],
10253
+ apple: "/apple-icon.png",
10254
+ },
10255
+ };
10256
+
10257
+ export default function RootLayout({
10258
+ children,
10259
+ }: Readonly<{
10260
+ children: React.ReactNode;
10261
+ }>) {
10262
+ return (
10263
+ <html lang="en" className={\`\${geistSans.variable} \${geistMono.variable}\`}>
10264
+ <body {...stylex.props(styles.body)}>
10265
+ <Providers>{children}</Providers>
10266
+ </body>
10267
+ </html>
10268
+ );
10269
+ }
10270
+
10271
+ const styles = stylex.create({
10272
+ body: {
10273
+ backgroundColor: colors.background,
10274
+ color: colors.foreground,
10275
+ fontFamily: fonts.sans,
10276
+ },
10277
+ });
10278
+ `
10279
+ },
10280
+ {
10281
+ when: (s) => !s.todoExample,
10282
+ filename: "apps/web/app/page.tsx",
10283
+ template: `'use client';
10284
+
10285
+ import { useQuery } from '@tanstack/react-query';
10286
+ import * as stylex from '@stylexjs/stylex';
10287
+ import { authClient } from '@/lib/auth-client';
10288
+ import { AuthForm } from '@/app/auth-form';
10289
+ import { Button } from '@<%= projectName %>/ui/button';
10290
+ import { Link } from '@<%= projectName %>/ui/link';
10291
+ import { Logo } from '@<%= projectName %>/ui/logo';
10292
+ import { colors, fonts, radii } from '@<%= projectName %>/ui/tokens.stylex';
10293
+
10294
+ export default function Home() {
10295
+ const { data: session, isPending } = authClient.useSession();
10296
+
10297
+ const { data: hello } = useQuery({
10298
+ queryKey: ['hello'],
10299
+ queryFn: () => fetch('/api/hello').then((r) => r.json()),
10300
+ });
10301
+
10302
+ return (
10303
+ <main {...stylex.props(styles.main)}>
10304
+ <div {...stylex.props(styles.header)}>
10305
+ <p {...stylex.props(styles.mono, styles.muted)}>apps/web/app/page.tsx</p>
10306
+ <p {...stylex.props(styles.muted)}>Delete me to get started!</p>
10307
+ </div>
10308
+
10309
+ <div {...stylex.props(styles.titleRow)}>
10310
+ <Logo style={styles.logo} />
10311
+ <div>
10312
+ <h1 {...stylex.props(styles.h1)}><%= projectName %></h1>
10313
+ <p {...stylex.props(styles.muted, styles.tagline)}>
10314
+ Next + Nest = Newt 💜
10315
+ </p>
10316
+ </div>
10317
+ </div>
10318
+
10319
+ <div {...stylex.props(styles.card)}>
10320
+ <p {...stylex.props(styles.eyebrow)}>next.js</p>
10321
+ <p {...stylex.props(styles.mono, styles.muted)}>apps/web/app/layout.tsx</p>
10322
+ <p {...stylex.props(styles.muted)}>Next.js rendering</p>
10323
+ </div>
10324
+
10325
+ <div {...stylex.props(styles.card)}>
10326
+ <p {...stylex.props(styles.eyebrow)}>nest.js</p>
10327
+ <p {...stylex.props(styles.mono, styles.muted)}>GET /api/hello</p>
10328
+ <pre {...stylex.props(styles.pre)}>
10329
+ <code>{JSON.stringify(hello, null, 2)}</code>
10330
+ </pre>
10331
+ </div>
10332
+
10333
+ <div {...stylex.props(styles.card)}>
10334
+ <p {...stylex.props(styles.eyebrow, styles.eyebrowSpaced)}>better-auth</p>
10335
+ {isPending ? (
10336
+ <p {...stylex.props(styles.muted)}>Loading…</p>
10337
+ ) : session ? (
10338
+ <div {...stylex.props(styles.signedIn)}>
10339
+ <p>Signed in as {session.user.name}</p>
10340
+ <Button onClick={() => void authClient.signOut()}>
10341
+ Sign out
10342
+ </Button>
10343
+ </div>
10344
+ ) : (
10345
+ <AuthForm />
10346
+ )}
10347
+ </div>
10348
+
10349
+ <div {...stylex.props(styles.footer)}>
10350
+ <p {...stylex.props(styles.muted)}>Learn more</p>
10351
+ <ul {...stylex.props(styles.list)}>
10352
+ <li>
10353
+ <Link href="https://newt-app.com">Documentation</Link>
10354
+ </li>
10355
+ <li>
10356
+ <Link href="https://github.com/zeevo/newt-app">GitHub</Link>
10357
+ </li>
10358
+ <li>
10359
+ <Link href="https://nextjs.org">Next.js</Link>
10360
+ </li>
10361
+ <li>
10362
+ <Link href="https://nestjs.com">NestJS</Link>
10363
+ </li>
10364
+ </ul>
10365
+ </div>
10366
+ </main>
10367
+ );
10368
+ }
10369
+
10370
+ const styles = stylex.create({
10371
+ main: {
10372
+ display: 'flex',
10373
+ flexDirection: 'column',
10374
+ gap: '1rem',
10375
+ maxWidth: '32rem',
10376
+ minHeight: '100%',
10377
+ marginInline: 'auto',
10378
+ paddingBlock: '2rem',
10379
+ paddingInline: '1rem',
10380
+ fontSize: '0.875rem',
10381
+ },
10382
+ header: {
10383
+ paddingBottom: '1rem',
10384
+ borderBottomColor: colors.border,
10385
+ borderBottomStyle: 'solid',
10386
+ borderBottomWidth: 1,
10387
+ },
10388
+ titleRow: {
10389
+ display: 'flex',
10390
+ alignItems: 'center',
10391
+ gap: '0.75rem',
10392
+ paddingBlock: '0.5rem',
10393
+ },
10394
+ logo: {
10395
+ width: '2.5rem',
10396
+ height: 'auto',
10397
+ },
10398
+ h1: {
10399
+ fontSize: '2.25rem',
10400
+ fontWeight: 900,
10401
+ letterSpacing: '-0.025em',
10402
+ },
10403
+ tagline: {
10404
+ letterSpacing: '0.1em',
10405
+ textTransform: 'uppercase',
10406
+ },
10407
+ card: {
10408
+ display: 'flex',
10409
+ flexDirection: 'column',
10410
+ gap: '0.25rem',
10411
+ padding: '1.5rem',
10412
+ borderColor: colors.border,
10413
+ borderRadius: radii.lg,
10414
+ borderStyle: 'solid',
10415
+ borderWidth: 1,
10416
+ },
10417
+ eyebrow: {
10418
+ fontFamily: fonts.mono,
10419
+ fontSize: '0.75rem',
10420
+ letterSpacing: '0.1em',
10421
+ textTransform: 'uppercase',
10422
+ color: colors.mutedForeground,
10423
+ },
10424
+ eyebrowSpaced: {
10425
+ marginBottom: '0.75rem',
10426
+ },
10427
+ pre: {
10428
+ marginTop: '0.5rem',
10429
+ padding: '0.75rem',
10430
+ fontFamily: fonts.mono,
10431
+ backgroundColor: 'oklch(0.269 0 0 / 0.5)',
10432
+ borderColor: colors.border,
10433
+ borderRadius: radii.sm,
10434
+ borderStyle: 'solid',
10435
+ borderWidth: 1,
10436
+ overflowX: 'auto',
10437
+ },
10438
+ signedIn: {
10439
+ display: 'flex',
10440
+ flexDirection: 'column',
10441
+ alignItems: 'flex-start',
10442
+ gap: '0.75rem',
10443
+ },
10444
+ footer: {
10445
+ display: 'flex',
10446
+ flexDirection: 'column',
10447
+ gap: '0.5rem',
10448
+ paddingBlock: '1rem',
10449
+ paddingInline: '0.5rem',
10450
+ },
10451
+ list: {
10452
+ display: 'flex',
10453
+ flexDirection: 'column',
10454
+ gap: '0.5rem',
10455
+ listStylePosition: 'inside',
10456
+ listStyleType: 'disc',
10457
+ },
10458
+ mono: {
10459
+ fontFamily: fonts.mono,
10460
+ },
10461
+ muted: {
10462
+ color: colors.mutedForeground,
10463
+ },
10464
+ });
10465
+ `
10466
+ },
10467
+ {
10468
+ filename: "apps/web/app/auth-form.tsx",
10469
+ template: `'use client';
10470
+
10471
+ import { useForm } from '@tanstack/react-form';
10472
+ import { useState } from 'react';
10473
+ import * as stylex from '@stylexjs/stylex';
10474
+ import { authClient } from '@/lib/auth-client';
10475
+ import { Button } from '@<%= projectName %>/ui/button';
10476
+ import { colors, radii } from '@<%= projectName %>/ui/tokens.stylex';
10477
+
10478
+ export function AuthForm() {
10479
+ const [tab, setTab] = useState<'signin' | 'signup'>('signup');
10480
+ const [error, setError] = useState('');
10481
+
10482
+ const form = useForm({
10483
+ defaultValues: { name: '', email: '', password: '' },
10484
+ onSubmit: async ({ value }) => {
10485
+ setError('');
10486
+ if (tab === 'signin') {
10487
+ const { error } = await authClient.signIn.email(value);
10488
+ if (error) setError(error.message ?? 'Sign in failed');
10489
+ } else {
10490
+ const { error } = await authClient.signUp.email(value);
10491
+ if (error) setError(error.message ?? 'Sign up failed');
10492
+ }
10493
+ },
10494
+ });
10495
+
10496
+ return (
10497
+ <>
10498
+ <div {...stylex.props(styles.tabs)}>
10499
+ <button
10500
+ onClick={() => setTab('signup')}
10501
+ {...stylex.props(styles.tab, tab === 'signup' && styles.tabActive)}
10502
+ >
10503
+ Sign up
10504
+ </button>
10505
+ <span {...stylex.props(styles.divider)}>|</span>
10506
+ <button
10507
+ onClick={() => setTab('signin')}
10508
+ {...stylex.props(styles.tab, tab === 'signin' && styles.tabActive)}
10509
+ >
10510
+ Sign in
10511
+ </button>
10512
+ </div>
10513
+
10514
+ <form
10515
+ onSubmit={(e) => {
10516
+ e.preventDefault();
10517
+ form.handleSubmit();
10518
+ }}
10519
+ {...stylex.props(styles.form)}
10520
+ >
10521
+ {tab === 'signup' && (
10522
+ <form.Field name="name">
10523
+ {(field) => (
10524
+ <div {...stylex.props(styles.field)}>
10525
+ <label {...stylex.props(styles.label)}>Name</label>
10526
+ <input
10527
+ type="text"
10528
+ autoComplete="name"
10529
+ value={field.state.value}
10530
+ onChange={(e) => field.handleChange(e.target.value)}
10531
+ required
10532
+ {...stylex.props(styles.input)}
10533
+ />
10534
+ </div>
10535
+ )}
10536
+ </form.Field>
10537
+ )}
10538
+
10539
+ <form.Field name="email">
10540
+ {(field) => (
10541
+ <div {...stylex.props(styles.field)}>
10542
+ <label {...stylex.props(styles.label)}>Email</label>
10543
+ <input
10544
+ type="email"
10545
+ autoComplete="email"
10546
+ value={field.state.value}
10547
+ onChange={(e) => field.handleChange(e.target.value)}
10548
+ required
10549
+ {...stylex.props(styles.input)}
10550
+ />
10551
+ </div>
10552
+ )}
10553
+ </form.Field>
10554
+
10555
+ <form.Field name="password">
10556
+ {(field) => (
10557
+ <div {...stylex.props(styles.field)}>
10558
+ <label {...stylex.props(styles.label)}>Password</label>
10559
+ <input
10560
+ type="password"
10561
+ autoComplete={
10562
+ tab === 'signin' ? 'current-password' : 'new-password'
10563
+ }
10564
+ value={field.state.value}
10565
+ onChange={(e) => field.handleChange(e.target.value)}
10566
+ required
10567
+ minLength={8}
10568
+ {...stylex.props(styles.input)}
10569
+ />
10570
+ </div>
10571
+ )}
10572
+ </form.Field>
10573
+
10574
+ {error && <p {...stylex.props(styles.error)}>{error}</p>}
10575
+
10576
+ <form.Subscribe selector={(s) => s.isSubmitting}>
10577
+ {(isSubmitting) => (
10578
+ <Button type="submit" disabled={isSubmitting} style={styles.submit}>
10579
+ {isSubmitting
10580
+ ? 'Loading…'
10581
+ : tab === 'signin'
10582
+ ? 'Sign in'
10583
+ : 'Create account'}
10584
+ </Button>
10585
+ )}
10586
+ </form.Subscribe>
10587
+ </form>
10588
+ </>
10589
+ );
10590
+ }
10591
+
10592
+ const styles = stylex.create({
10593
+ tabs: {
10594
+ display: 'flex',
10595
+ gap: '0.5rem',
10596
+ marginBottom: '1.5rem',
10597
+ fontSize: '0.875rem',
10598
+ },
10599
+ tab: {
10600
+ color: {
10601
+ default: colors.mutedForeground,
10602
+ ':hover': colors.foreground,
10603
+ },
10604
+ backgroundColor: 'transparent',
10605
+ borderStyle: 'none',
10606
+ cursor: 'pointer',
10607
+ padding: 0,
10608
+ fontFamily: 'inherit',
10609
+ fontSize: 'inherit',
10610
+ },
10611
+ tabActive: {
10612
+ color: colors.foreground,
10613
+ fontWeight: 600,
10614
+ },
10615
+ divider: {
10616
+ color: colors.border,
10617
+ },
10618
+ form: {
10619
+ display: 'flex',
10620
+ flexDirection: 'column',
10621
+ gap: '1rem',
10622
+ },
10623
+ field: {
10624
+ display: 'flex',
10625
+ flexDirection: 'column',
10626
+ gap: '0.25rem',
10627
+ },
10628
+ label: {
10629
+ fontSize: '0.875rem',
10630
+ fontWeight: 500,
10631
+ },
10632
+ input: {
10633
+ width: '100%',
10634
+ height: 36,
10635
+ paddingBlock: '0.25rem',
10636
+ paddingInline: '0.75rem',
10637
+ fontFamily: 'inherit',
10638
+ fontSize: '0.875rem',
10639
+ color: colors.foreground,
10640
+ backgroundColor: 'oklch(0.269 0 0 / 0.5)',
10641
+ borderColor: {
10642
+ default: colors.border,
10643
+ ':focus-visible': colors.mutedForeground,
10644
+ },
10645
+ borderRadius: radii.sm,
10646
+ borderStyle: 'solid',
10647
+ borderWidth: 1,
10648
+ outlineStyle: 'none',
10649
+ transitionProperty: 'border-color',
10650
+ transitionDuration: '150ms',
10651
+ },
10652
+ error: {
10653
+ fontSize: '0.875rem',
10654
+ color: colors.danger,
10655
+ },
10656
+ submit: {
10657
+ width: '100%',
10658
+ },
10659
+ });
10660
+ `
10661
+ }
10662
+ ] };
10663
+ //#endregion
10664
+ //#region src/templates/eslint-config/templates/package-json.ts
10665
+ var package_json_default$1 = {
10666
+ filename: "packages/eslint-config/package.json",
10667
+ template: `{
10668
+ "name": "@<%= projectName %>/eslint-config",
10669
+ "version": "0.0.0",
10670
+ "type": "module",
10671
+ "private": true,
10672
+ "exports": {
10673
+ "./base": "./base.js",
10674
+ "./next-js": "./next.js",
10675
+ "./react-internal": "./react-internal.js"
10676
+ },
10677
+ "devDependencies": {
10678
+ "@eslint/js": "<%= versions["@eslint/js"] %>",
10679
+ "@next/eslint-plugin-next": "<%= versions["@next/eslint-plugin-next"] %>",
10680
+ "eslint": "<%= versions.eslint %>",
10681
+ "eslint-config-prettier": "<%= versions["eslint-config-prettier"] %>",
10682
+ "eslint-plugin-only-warn": "<%= versions["eslint-plugin-only-warn"] %>",
10683
+ "eslint-plugin-react": "<%= versions["eslint-plugin-react"] %>",
10684
+ "eslint-plugin-react-hooks": "<%= versions["eslint-plugin-react-hooks"] %>",
10685
+ "eslint-plugin-turbo": "<%= versions["eslint-plugin-turbo"] %>",
10686
+ "globals": "<%= versions.globals %>",
10687
+ "typescript": "<%= versions.typescript %>",
10688
+ "typescript-eslint": "<%= versions["typescript-eslint"] %>"
10689
+ }
10690
+ }`
10691
+ };
10692
+ //#endregion
10693
+ //#region src/templates/eslint-config/templates/base.ts
10694
+ var base_default$1 = {
10695
+ filename: "packages/eslint-config/base.js",
10696
+ template: `import js from "@eslint/js";
10697
+ import eslintConfigPrettier from "eslint-config-prettier";
10698
+ import turboPlugin from "eslint-plugin-turbo";
10699
+ import tseslint from "typescript-eslint";
10700
+ import onlyWarn from "eslint-plugin-only-warn";
10701
+
10702
+ /**
10703
+ * A shared ESLint configuration for the repository.
10704
+ *
10705
+ * @type {import("eslint").Linter.Config[]}
10706
+ * */
10707
+ export const config = [
10708
+ js.configs.recommended,
10709
+ eslintConfigPrettier,
10710
+ ...tseslint.configs.recommended,
10711
+ {
10712
+ plugins: {
10713
+ turbo: turboPlugin,
10714
+ },
10715
+ rules: {
10716
+ "turbo/no-undeclared-env-vars": "warn",
10717
+ },
10718
+ },
10719
+ {
10720
+ plugins: {
10721
+ onlyWarn,
10722
+ },
10723
+ },
10724
+ {
10725
+ ignores: ["dist/**"],
10726
+ },
10727
+ ];`
10728
+ };
10729
+ //#endregion
10730
+ //#region src/templates/eslint-config/templates/next.ts
10731
+ var next_default = {
10732
+ filename: "packages/eslint-config/next.js",
10733
+ template: `import js from "@eslint/js";
10734
+ import { globalIgnores } from "eslint/config";
10735
+ import eslintConfigPrettier from "eslint-config-prettier";
10736
+ import tseslint from "typescript-eslint";
10737
+ import pluginReactHooks from "eslint-plugin-react-hooks";
10738
+ import pluginReact from "eslint-plugin-react";
10739
+ import globals from "globals";
10740
+ import pluginNext from "@next/eslint-plugin-next";
10741
+ import { config as baseConfig } from "./base.js";
10742
+
10743
+ /**
10744
+ * A custom ESLint configuration for libraries that use Next.js.
10745
+ *
10746
+ * @type {import("eslint").Linter.Config[]}
10747
+ * */
10748
+ export const nextJsConfig = [
10749
+ ...baseConfig,
10750
+ js.configs.recommended,
10751
+ eslintConfigPrettier,
10752
+ ...tseslint.configs.recommended,
10753
+ globalIgnores([
10754
+ // Default ignores of eslint-config-next:
10755
+ ".next/**",
10756
+ "out/**",
10757
+ "build/**",
10758
+ "next-env.d.ts",
10759
+ ]),
10760
+ {
10761
+ ...pluginReact.configs.flat.recommended,
10762
+ languageOptions: {
10763
+ ...pluginReact.configs.flat.recommended.languageOptions,
10764
+ globals: {
10765
+ ...globals.serviceworker,
10766
+ ...globals.node,
10767
+ },
10768
+ },
10769
+ },
10770
+ {
10771
+ plugins: {
10772
+ "@next/next": pluginNext,
10773
+ },
10774
+ rules: {
10775
+ ...pluginNext.configs.recommended.rules,
10776
+ ...pluginNext.configs["core-web-vitals"].rules,
10777
+ },
10778
+ },
10779
+ {
10780
+ plugins: {
10781
+ "react-hooks": pluginReactHooks,
10782
+ },
10783
+ settings: { react: { version: "detect" } },
10784
+ rules: {
10785
+ ...pluginReactHooks.configs.recommended.rules,
10786
+ // React scope no longer necessary with new JSX transform.
10787
+ "react/react-in-jsx-scope": "off",
10788
+ },
10789
+ },
10790
+ ];`
10791
+ };
10792
+ //#endregion
10793
+ //#region src/templates/eslint-config/templates/react-internal.ts
10794
+ var react_internal_default = {
10795
+ filename: "packages/eslint-config/react-internal.js",
10796
+ template: `import js from "@eslint/js";
10797
+ import eslintConfigPrettier from "eslint-config-prettier";
10798
+ import tseslint from "typescript-eslint";
10799
+ import pluginReactHooks from "eslint-plugin-react-hooks";
10800
+ import pluginReact from "eslint-plugin-react";
10801
+ import globals from "globals";
10802
+ import { config as baseConfig } from "./base.js";
10803
+
10804
+ /**
10805
+ * A custom ESLint configuration for libraries that use React.
10806
+ *
10807
+ * @type {import("eslint").Linter.Config[]} */
10808
+ export const config = [
10809
+ ...baseConfig,
9978
10810
  js.configs.recommended,
9979
10811
  eslintConfigPrettier,
9980
10812
  ...tseslint.configs.recommended,
@@ -10500,7 +11332,9 @@ export default {
10500
11332
  rootDir: 'src',
10501
11333
  testRegex: '.*\\\\.spec\\\\.ts$',
10502
11334
  transform: {
10503
- '^.+\\\\.(t|j)s$': 'ts-jest',
11335
+ // Under nodenext ts-jest follows each package's "type", so it would emit
11336
+ // packages/db as ESM, which jest's CommonJS runtime can't require
11337
+ '^.+\\\\.(t|j)s$': ['ts-jest', { tsconfig: { module: 'commonjs' } }],
10504
11338
  },
10505
11339
  collectCoverageFrom: ['**/*.(t|j)s'],
10506
11340
  coverageDirectory: '../coverage',
@@ -11095,6 +11929,7 @@ export async function GET() {
11095
11929
  }`
11096
11930
  },
11097
11931
  {
11932
+ when: (s) => !s.stylex,
11098
11933
  filename: "apps/web/package.json",
11099
11934
  template: `{
11100
11935
  "name": "web",
@@ -11408,7 +12243,7 @@ import { AppService } from './app.service';
11408
12243
  // serves apps/web's static export; the api runs from apps/api
11409
12244
  ServeStaticModule.forRoot({
11410
12245
  rootPath: join(process.cwd(), '../web/out'),
11411
- exclude: ['/api/{*splat}'],
12246
+ exclude: ['/api/{*splat}', '/{*path}.:ext'],
11412
12247
  }),
11413
12248
  AuthModule.forRoot({ auth }),
11414
12249
  ],
@@ -11722,7 +12557,7 @@ import { TodosModule } from './todos/todos.module';
11722
12557
  // serves apps/web's static export; the api runs from apps/api
11723
12558
  ServeStaticModule.forRoot({
11724
12559
  rootPath: join(process.cwd(), '../web/out'),
11725
- exclude: ['/api/{*splat}'],
12560
+ exclude: ['/api/{*splat}', '/{*path}.:ext'],
11726
12561
  }),
11727
12562
  AuthModule.forRoot({ auth }),
11728
12563
  TodosModule,
@@ -11968,7 +12803,7 @@ export function TodoList({
11968
12803
  //#endregion
11969
12804
  //#region src/templates/todo-example/templates/web-page.ts
11970
12805
  var web_page_default = {
11971
- when: (s) => s.todoExample && !s.shadcn,
12806
+ when: (s) => s.todoExample && !s.shadcn && !s.stylex,
11972
12807
  filename: "apps/web/app/page.tsx",
11973
12808
  template: `'use client';
11974
12809
 
@@ -12306,6 +13141,424 @@ export default function Home() {
12306
13141
  }`
12307
13142
  };
12308
13143
  //#endregion
13144
+ //#region src/templates/todo-example/templates/stylex-todo-list.ts
13145
+ var stylex_todo_list_default = {
13146
+ filename: "apps/web/app/todo-list.tsx",
13147
+ template: `'use client';
13148
+
13149
+ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
13150
+ import { useForm } from '@tanstack/react-form';
13151
+ import * as stylex from '@stylexjs/stylex';
13152
+ import { authClient } from '@/lib/auth-client';
13153
+ import { Button } from '@<%= projectName %>/ui/button';
13154
+ import { colors, radii } from '@<%= projectName %>/ui/tokens.stylex';
13155
+
13156
+ interface Todo {
13157
+ id: string;
13158
+ title: string;
13159
+ done: boolean;
13160
+ }
13161
+
13162
+ const api = {
13163
+ getTodos: (): Promise<Todo[]> => fetch('/api/todos').then((r) => r.json()),
13164
+ createTodo: (title: string): Promise<Todo> =>
13165
+ fetch('/api/todos', {
13166
+ method: 'POST',
13167
+ headers: { 'Content-Type': 'application/json' },
13168
+ body: JSON.stringify({ title }),
13169
+ }).then((r) => r.json()),
13170
+ toggleTodo: (id: string): Promise<Todo> =>
13171
+ fetch(\`/api/todos/\${id}/toggle\`, { method: 'PATCH' }).then((r) => r.json()),
13172
+ deleteTodo: (id: string): Promise<void> =>
13173
+ fetch(\`/api/todos/\${id}\`, { method: 'DELETE' }).then(() => undefined),
13174
+ };
13175
+
13176
+ export function TodoList({
13177
+ session,
13178
+ }: {
13179
+ session: { user: { email: string } };
13180
+ }) {
13181
+ const queryClient = useQueryClient();
13182
+
13183
+ const { data: todos = [], isPending } = useQuery({
13184
+ queryKey: ['todos'],
13185
+ queryFn: api.getTodos,
13186
+ });
13187
+
13188
+ const createMutation = useMutation({
13189
+ mutationFn: api.createTodo,
13190
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
13191
+ });
13192
+
13193
+ const toggleMutation = useMutation({
13194
+ mutationFn: api.toggleTodo,
13195
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
13196
+ });
13197
+
13198
+ const deleteMutation = useMutation({
13199
+ mutationFn: api.deleteTodo,
13200
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
13201
+ });
13202
+
13203
+ const form = useForm({
13204
+ defaultValues: { title: '' },
13205
+ onSubmit: async ({ value }) => {
13206
+ if (!value.title.trim()) return;
13207
+ createMutation.mutate(value.title.trim());
13208
+ form.reset();
13209
+ },
13210
+ });
13211
+
13212
+ return (
13213
+ <>
13214
+ <div {...stylex.props(styles.header)}>
13215
+ <h1 {...stylex.props(styles.h1)}>Todos</h1>
13216
+ <div {...stylex.props(styles.session)}>
13217
+ <span>{session.user.email}</span>
13218
+ <button
13219
+ {...stylex.props(styles.signOut)}
13220
+ onClick={() => authClient.signOut()}
13221
+ >
13222
+ Sign out
13223
+ </button>
13224
+ </div>
13225
+ </div>
13226
+
13227
+ <form
13228
+ onSubmit={(e) => {
13229
+ e.preventDefault();
13230
+ form.handleSubmit();
13231
+ }}
13232
+ {...stylex.props(styles.form)}
13233
+ >
13234
+ <form.Field name="title">
13235
+ {(field) => (
13236
+ <input
13237
+ value={field.state.value}
13238
+ onChange={(e) => field.handleChange(e.target.value)}
13239
+ placeholder="New todo…"
13240
+ {...stylex.props(styles.input)}
13241
+ />
13242
+ )}
13243
+ </form.Field>
13244
+
13245
+ <form.Subscribe selector={(s) => s.isSubmitting}>
13246
+ {(isSubmitting) => (
13247
+ <Button type="submit" disabled={isSubmitting || createMutation.isPending}>
13248
+ Add
13249
+ </Button>
13250
+ )}
13251
+ </form.Subscribe>
13252
+ </form>
13253
+
13254
+ {isPending ? (
13255
+ <p {...stylex.props(styles.muted)}>Loading…</p>
13256
+ ) : (
13257
+ <ul {...stylex.props(styles.list)}>
13258
+ {todos.map((todo) => (
13259
+ <li key={todo.id} {...stylex.props(styles.item)}>
13260
+ <input
13261
+ type="checkbox"
13262
+ checked={todo.done}
13263
+ onChange={() => toggleMutation.mutate(todo.id)}
13264
+ />
13265
+ <span {...stylex.props(styles.title, todo.done && styles.titleDone)}>
13266
+ {todo.title}
13267
+ </span>
13268
+ <button
13269
+ onClick={() => deleteMutation.mutate(todo.id)}
13270
+ {...stylex.props(styles.remove)}
13271
+ >
13272
+ ×
13273
+ </button>
13274
+ </li>
13275
+ ))}
13276
+ </ul>
13277
+ )}
13278
+
13279
+ {!isPending && todos.length === 0 && (
13280
+ <p {...stylex.props(styles.muted)}>No todos yet.</p>
13281
+ )}
13282
+ </>
13283
+ );
13284
+ }
13285
+
13286
+ const styles = stylex.create({
13287
+ header: {
13288
+ display: 'flex',
13289
+ alignItems: 'center',
13290
+ justifyContent: 'space-between',
13291
+ marginBottom: '2rem',
13292
+ },
13293
+ h1: {
13294
+ fontSize: '1.5rem',
13295
+ fontWeight: 600,
13296
+ },
13297
+ session: {
13298
+ display: 'flex',
13299
+ alignItems: 'center',
13300
+ gap: '0.75rem',
13301
+ fontSize: '0.875rem',
13302
+ color: colors.mutedForeground,
13303
+ },
13304
+ signOut: {
13305
+ color: {
13306
+ default: 'inherit',
13307
+ ':hover': colors.foreground,
13308
+ },
13309
+ backgroundColor: 'transparent',
13310
+ borderStyle: 'none',
13311
+ cursor: 'pointer',
13312
+ padding: 0,
13313
+ fontFamily: 'inherit',
13314
+ fontSize: 'inherit',
13315
+ },
13316
+ form: {
13317
+ display: 'flex',
13318
+ gap: '0.5rem',
13319
+ marginBottom: '1.5rem',
13320
+ },
13321
+ input: {
13322
+ flexGrow: 1,
13323
+ height: 36,
13324
+ paddingBlock: '0.25rem',
13325
+ paddingInline: '0.75rem',
13326
+ fontFamily: 'inherit',
13327
+ fontSize: '0.875rem',
13328
+ color: colors.foreground,
13329
+ backgroundColor: 'oklch(0.269 0 0 / 0.5)',
13330
+ borderColor: {
13331
+ default: colors.border,
13332
+ ':focus-visible': colors.mutedForeground,
13333
+ },
13334
+ borderRadius: radii.sm,
13335
+ borderStyle: 'solid',
13336
+ borderWidth: 1,
13337
+ outlineStyle: 'none',
13338
+ transitionProperty: 'border-color',
13339
+ transitionDuration: '150ms',
13340
+ },
13341
+ list: {
13342
+ listStyleType: 'none',
13343
+ },
13344
+ item: {
13345
+ display: 'flex',
13346
+ alignItems: 'center',
13347
+ gap: '0.75rem',
13348
+ paddingBlock: '0.75rem',
13349
+ borderTopColor: colors.border,
13350
+ borderTopStyle: 'solid',
13351
+ borderTopWidth: {
13352
+ default: 1,
13353
+ ':first-child': 0,
13354
+ },
13355
+ },
13356
+ title: {
13357
+ flexGrow: 1,
13358
+ fontSize: '0.875rem',
13359
+ },
13360
+ titleDone: {
13361
+ color: colors.mutedForeground,
13362
+ textDecorationLine: 'line-through',
13363
+ },
13364
+ remove: {
13365
+ color: {
13366
+ default: colors.mutedForeground,
13367
+ ':hover': colors.danger,
13368
+ },
13369
+ backgroundColor: 'transparent',
13370
+ borderStyle: 'none',
13371
+ cursor: 'pointer',
13372
+ padding: 0,
13373
+ fontSize: '1.125rem',
13374
+ lineHeight: 1,
13375
+ },
13376
+ muted: {
13377
+ fontSize: '0.875rem',
13378
+ color: colors.mutedForeground,
13379
+ },
13380
+ });
13381
+ `
13382
+ };
13383
+ //#endregion
13384
+ //#region src/templates/todo-example/templates/stylex-page.ts
13385
+ var stylex_page_default = {
13386
+ when: (s) => s.todoExample && s.stylex,
13387
+ filename: "apps/web/app/page.tsx",
13388
+ template: `'use client';
13389
+
13390
+ import { useQuery } from '@tanstack/react-query';
13391
+ import * as stylex from '@stylexjs/stylex';
13392
+ import { authClient } from '@/lib/auth-client';
13393
+ import { AuthForm } from '@/app/auth-form';
13394
+ import { Link } from '@<%= projectName %>/ui/link';
13395
+ import { Logo } from '@<%= projectName %>/ui/logo';
13396
+ import { TodoList } from '@/app/todo-list';
13397
+ import { colors, fonts, radii } from '@<%= projectName %>/ui/tokens.stylex';
13398
+
13399
+ export default function Home() {
13400
+ const { data: session, isPending } = authClient.useSession();
13401
+
13402
+ const { data: hello } = useQuery({
13403
+ queryKey: ['hello'],
13404
+ queryFn: () => fetch('/api/hello').then((r) => r.json()),
13405
+ });
13406
+
13407
+ return (
13408
+ <main {...stylex.props(styles.main)}>
13409
+ <div {...stylex.props(styles.header)}>
13410
+ <p {...stylex.props(styles.mono, styles.muted)}>apps/web/app/page.tsx</p>
13411
+ <p {...stylex.props(styles.muted)}>Delete me to get started!</p>
13412
+ </div>
13413
+
13414
+ <div {...stylex.props(styles.titleRow)}>
13415
+ <Logo style={styles.logo} />
13416
+ <div>
13417
+ <h1 {...stylex.props(styles.h1)}><%= projectName %></h1>
13418
+ <p {...stylex.props(styles.muted, styles.tagline)}>
13419
+ Next + Nest = Newt 💜
13420
+ </p>
13421
+ </div>
13422
+ </div>
13423
+
13424
+ <div {...stylex.props(styles.card)}>
13425
+ <p {...stylex.props(styles.eyebrow)}>next.js</p>
13426
+ <p {...stylex.props(styles.mono, styles.muted)}>apps/web/app/layout.tsx</p>
13427
+ <p {...stylex.props(styles.muted)}>Next.js rendering</p>
13428
+ </div>
13429
+
13430
+ <div {...stylex.props(styles.card)}>
13431
+ <p {...stylex.props(styles.eyebrow)}>nest.js</p>
13432
+ <p {...stylex.props(styles.mono, styles.muted)}>GET /api/hello</p>
13433
+ <pre {...stylex.props(styles.pre)}>
13434
+ <code>{JSON.stringify(hello, null, 2)}</code>
13435
+ </pre>
13436
+ </div>
13437
+
13438
+ <div {...stylex.props(styles.card)}>
13439
+ <p {...stylex.props(styles.eyebrow, styles.eyebrowSpaced)}>better-auth</p>
13440
+ {isPending ? (
13441
+ <p {...stylex.props(styles.muted)}>Loading…</p>
13442
+ ) : session ? (
13443
+ <TodoList session={session} />
13444
+ ) : (
13445
+ <AuthForm />
13446
+ )}
13447
+ </div>
13448
+
13449
+ <div {...stylex.props(styles.footer)}>
13450
+ <p {...stylex.props(styles.muted)}>Learn more</p>
13451
+ <ul {...stylex.props(styles.list)}>
13452
+ <li>
13453
+ <Link href="https://newt-app.com">Documentation</Link>
13454
+ </li>
13455
+ <li>
13456
+ <Link href="https://github.com/zeevo/newt-app">GitHub</Link>
13457
+ </li>
13458
+ <li>
13459
+ <Link href="https://nextjs.org">Next.js</Link>
13460
+ </li>
13461
+ <li>
13462
+ <Link href="https://nestjs.com">NestJS</Link>
13463
+ </li>
13464
+ </ul>
13465
+ </div>
13466
+ </main>
13467
+ );
13468
+ }
13469
+
13470
+ const styles = stylex.create({
13471
+ main: {
13472
+ display: 'flex',
13473
+ flexDirection: 'column',
13474
+ gap: '1rem',
13475
+ maxWidth: '32rem',
13476
+ minHeight: '100%',
13477
+ marginInline: 'auto',
13478
+ paddingBlock: '2rem',
13479
+ paddingInline: '1rem',
13480
+ fontSize: '0.875rem',
13481
+ },
13482
+ header: {
13483
+ paddingBottom: '1rem',
13484
+ borderBottomColor: colors.border,
13485
+ borderBottomStyle: 'solid',
13486
+ borderBottomWidth: 1,
13487
+ },
13488
+ titleRow: {
13489
+ display: 'flex',
13490
+ alignItems: 'center',
13491
+ gap: '0.75rem',
13492
+ paddingBlock: '0.5rem',
13493
+ },
13494
+ logo: {
13495
+ width: '2.5rem',
13496
+ height: 'auto',
13497
+ },
13498
+ h1: {
13499
+ fontSize: '2.25rem',
13500
+ fontWeight: 900,
13501
+ letterSpacing: '-0.025em',
13502
+ },
13503
+ tagline: {
13504
+ letterSpacing: '0.1em',
13505
+ textTransform: 'uppercase',
13506
+ },
13507
+ card: {
13508
+ display: 'flex',
13509
+ flexDirection: 'column',
13510
+ gap: '0.25rem',
13511
+ padding: '1.5rem',
13512
+ borderColor: colors.border,
13513
+ borderRadius: radii.lg,
13514
+ borderStyle: 'solid',
13515
+ borderWidth: 1,
13516
+ },
13517
+ eyebrow: {
13518
+ fontFamily: fonts.mono,
13519
+ fontSize: '0.75rem',
13520
+ letterSpacing: '0.1em',
13521
+ textTransform: 'uppercase',
13522
+ color: colors.mutedForeground,
13523
+ },
13524
+ eyebrowSpaced: {
13525
+ marginBottom: '0.75rem',
13526
+ },
13527
+ pre: {
13528
+ marginTop: '0.5rem',
13529
+ padding: '0.75rem',
13530
+ fontFamily: fonts.mono,
13531
+ backgroundColor: 'oklch(0.269 0 0 / 0.5)',
13532
+ borderColor: colors.border,
13533
+ borderRadius: radii.sm,
13534
+ borderStyle: 'solid',
13535
+ borderWidth: 1,
13536
+ overflowX: 'auto',
13537
+ },
13538
+ footer: {
13539
+ display: 'flex',
13540
+ flexDirection: 'column',
13541
+ gap: '0.5rem',
13542
+ paddingBlock: '1rem',
13543
+ paddingInline: '0.5rem',
13544
+ },
13545
+ list: {
13546
+ display: 'flex',
13547
+ flexDirection: 'column',
13548
+ gap: '0.5rem',
13549
+ listStylePosition: 'inside',
13550
+ listStyleType: 'disc',
13551
+ },
13552
+ mono: {
13553
+ fontFamily: fonts.mono,
13554
+ },
13555
+ muted: {
13556
+ color: colors.mutedForeground,
13557
+ },
13558
+ });
13559
+ `
13560
+ };
13561
+ //#endregion
12309
13562
  //#region src/templates/todo-example/index.ts
12310
13563
  const todoExampleApi = { templates: [
12311
13564
  todos_module_default,
@@ -12328,6 +13581,7 @@ const todoExampleDi = { templates: [
12328
13581
  ] };
12329
13582
  const todoExampleWeb = { templates: [web_todo_list_default, web_page_default] };
12330
13583
  const todoExampleShadcn = { templates: [shadcn_todo_list_default, shadcn_page_default] };
13584
+ const todoExampleStylex = { templates: [stylex_todo_list_default, stylex_page_default] };
12331
13585
  //#endregion
12332
13586
  //#region src/templates/index.ts
12333
13587
  const staticDir = new URL("./static/", import.meta.url);
@@ -12341,7 +13595,7 @@ const E2E_FILES = [
12341
13595
  "apps/api/vitest.config.e2e.mts"
12342
13596
  ];
12343
13597
  function selectModules(selection) {
12344
- const { deployment, nest, todoExample, shadcn, database, linter, testing, extras } = selection;
13598
+ const { deployment, nest, todoExample, shadcn, stylex, database, linter, testing, extras } = selection;
12345
13599
  const deploymentModule = deployment === "standalone" ? deploymentStandalone : deployment === "spa" ? singleProcessStaticExport : null;
12346
13600
  const webModule = deployment === "spa" ? {
12347
13601
  ...web$1,
@@ -12360,7 +13614,7 @@ function selectModules(selection) {
12360
13614
  ...nest === "off" ? [] : [api$1],
12361
13615
  database === "postgres" ? dbPostgres : dbSqlite,
12362
13616
  auth,
12363
- shadcn ? shadcnUi : ui$1,
13617
+ shadcn ? shadcnUi : stylex ? stylexUi : ui$1,
12364
13618
  linter === "oxc" ? oxc : eslintConfig,
12365
13619
  ...extras.includes("anti-slop") ? [antiSlop] : [],
12366
13620
  typescriptConfig,
@@ -12371,7 +13625,7 @@ function selectModules(selection) {
12371
13625
  ...todoExample ? [
12372
13626
  todoExampleApi,
12373
13627
  ...nest === "di-only" ? [todoExampleDi] : [todoExampleControllers],
12374
- shadcn ? todoExampleShadcn : todoExampleWeb
13628
+ shadcn ? todoExampleShadcn : stylex ? todoExampleStylex : todoExampleWeb
12375
13629
  ] : []
12376
13630
  ];
12377
13631
  }
@@ -12478,6 +13732,13 @@ function validateExtrasCombo(extras, linter) {
12478
13732
  };
12479
13733
  return { valid: true };
12480
13734
  }
13735
+ function validateStylingCombo(shadcn, stylex) {
13736
+ if (shadcn && stylex) return {
13737
+ valid: false,
13738
+ error: "--shadcn cannot be combined with --stylex.\nshadcn/ui components are written in Tailwind utility classes, which StyleX does\nnot compile. Pick one styling system: drop --stylex for shadcn/ui, or drop\n--shadcn for StyleX."
13739
+ };
13740
+ return { valid: true };
13741
+ }
12481
13742
  function validateDeploymentCombo(deployment, nest) {
12482
13743
  if (deployment === "spa" && nest === "di-only") return {
12483
13744
  valid: false,
@@ -12633,6 +13894,7 @@ function buildPayload(report) {
12633
13894
  ci: detectCi(),
12634
13895
  explicitFlags: [...report.explicitFlags].sort().join(","),
12635
13896
  shadcn: selection.shadcn,
13897
+ stylex: selection.stylex,
12636
13898
  testing: selection.testing,
12637
13899
  database: selection.database,
12638
13900
  linter: selection.linter,
@@ -12706,6 +13968,10 @@ async function doInit(options) {
12706
13968
  message: "Use shadcn/ui?",
12707
13969
  initialValue: true
12708
13970
  }),
13971
+ stylex: ({ results }) => results.shadcn ? void 0 : p.confirm({
13972
+ message: "Use StyleX instead of Tailwind?",
13973
+ initialValue: false
13974
+ }),
12709
13975
  nest: () => p.select({
12710
13976
  message: "NestJS?",
12711
13977
  options: [
@@ -12811,6 +14077,7 @@ async function doInit(options) {
12811
14077
  process.exit(0);
12812
14078
  } });
12813
14079
  const useShadcn = options.nonInteractive ? options.shadcn : answers.shadcn ?? true;
14080
+ const useStylex = options.nonInteractive ? options.stylex : answers.stylex ?? false;
12814
14081
  const testing = options.nonInteractive ? options.testing : answers.testing ?? "jest";
12815
14082
  const database = options.nonInteractive ? options.database : answers.database ?? "sqlite";
12816
14083
  const linter = options.nonInteractive ? options.linter : answers.linter ?? "eslint";
@@ -12818,6 +14085,8 @@ async function doInit(options) {
12818
14085
  const nest = options.nonInteractive ? options.nest : answers.nest ?? "on";
12819
14086
  const todoExample = options.nonInteractive ? options.includeExample : nest !== "off" && (answers.todoExample ?? true);
12820
14087
  const extras = options.nonInteractive ? options.extras : answers.extras ?? [];
14088
+ const stylingCombo = validateStylingCombo(useShadcn, useStylex);
14089
+ if (!stylingCombo.valid) throw new Error(stylingCombo.error);
12821
14090
  const deploymentCombo = validateDeploymentCombo(deployment, nest);
12822
14091
  if (!deploymentCombo.valid) throw new Error(deploymentCombo.error);
12823
14092
  const exampleCombo = validateExampleCombo(todoExample, nest);
@@ -12829,6 +14098,7 @@ async function doInit(options) {
12829
14098
  nest,
12830
14099
  todoExample,
12831
14100
  shadcn: useShadcn,
14101
+ stylex: useStylex,
12832
14102
  database,
12833
14103
  linter,
12834
14104
  testing,
@@ -12897,10 +14167,11 @@ async function doInit(options) {
12897
14167
  }
12898
14168
  }
12899
14169
  const program = new Command();
12900
- program.name("create-newt-app").version(version).description("Create a new newt-app monorepo").argument("[name]").option("-ni, --no-install", "Skip pnpm install", true).option("-ng, --no-git", "Skip git initialization", true).option("--shadcn", "Include shadcn/ui", false).option("--testing <framework>", "Testing framework: vitest or jest", "jest").option("--database <database>", "Database: sqlite or postgres", "sqlite").option("--linter <linter>", "Linter: eslint or oxc", "eslint").option("--deployment <strategy>", "Deployment: none, standalone, or spa", "none").option("--nest <mode>", "NestJS: on, off, or di-only", "on").option("--include-example", "Include the todo example", false).option("--extras <list>", "Extras, comma-separated: anti-slop", "").action(async (name, options, command) => {
14170
+ program.name("create-newt-app").version(version).description("Create a new newt-app monorepo").argument("[name]").option("-ni, --no-install", "Skip pnpm install", true).option("-ng, --no-git", "Skip git initialization", true).option("--shadcn", "Include shadcn/ui", false).option("--stylex", "Use StyleX instead of Tailwind", false).option("--testing <framework>", "Testing framework: vitest or jest", "jest").option("--database <database>", "Database: sqlite or postgres", "sqlite").option("--linter <linter>", "Linter: eslint or oxc", "eslint").option("--deployment <strategy>", "Deployment: none, standalone, or spa", "none").option("--nest <mode>", "NestJS: on, off, or di-only", "on").option("--include-example", "Include the todo example", false).option("--extras <list>", "Extras, comma-separated: anti-slop", "").action(async (name, options, command) => {
12901
14171
  intro(`Create a ${chalk.blue("newt")} app.`);
12902
14172
  const explicitFlags = Object.entries({
12903
14173
  shadcn: "--shadcn",
14174
+ stylex: "--stylex",
12904
14175
  testing: "--testing",
12905
14176
  database: "--database",
12906
14177
  linter: "--linter",
@@ -12953,6 +14224,7 @@ program.name("create-newt-app").version(version).description("Create a new newt-
12953
14224
  git: options.git,
12954
14225
  nonInteractive,
12955
14226
  shadcn: options.shadcn,
14227
+ stylex: options.stylex,
12956
14228
  testing: options.testing,
12957
14229
  database: options.database,
12958
14230
  linter: options.linter,