feat(create-turbo): create kitchen-sink

This commit is contained in:
Turbobot 2025-06-19 02:40:01 +08:00 committed by afiqzudinhadi
commit 4625c93980
80 changed files with 11572 additions and 0 deletions

View file

@ -0,0 +1,3 @@
# `@turbo/eslint-config`
Collection of internal eslint configurations.

View file

@ -0,0 +1,32 @@
import js from "@eslint/js";
import eslintConfigPrettier from "eslint-config-prettier";
import turboPlugin from "eslint-plugin-turbo";
import tseslint from "typescript-eslint";
import onlyWarn from "eslint-plugin-only-warn";
/**
* A shared ESLint configuration for the repository.
*
* @type {import("eslint").Linter.Config}
* */
export const config = [
js.configs.recommended,
eslintConfigPrettier,
...tseslint.configs.recommended,
{
plugins: {
turbo: turboPlugin,
},
rules: {
"turbo/no-undeclared-env-vars": "warn",
},
},
{
plugins: {
onlyWarn,
},
},
{
ignores: ["dist/**"],
},
];

View file

@ -0,0 +1,49 @@
import js from "@eslint/js";
import eslintConfigPrettier from "eslint-config-prettier";
import tseslint from "typescript-eslint";
import pluginReactHooks from "eslint-plugin-react-hooks";
import pluginReact from "eslint-plugin-react";
import globals from "globals";
import pluginNext from "@next/eslint-plugin-next";
import { config as baseConfig } from "./index.js";
/**
* A custom ESLint configuration for libraries that use Next.js.
*
* @type {import("eslint").Linter.Config}
* */
export const config = [
...baseConfig,
js.configs.recommended,
eslintConfigPrettier,
...tseslint.configs.recommended,
{
...pluginReact.configs.flat.recommended,
languageOptions: {
...pluginReact.configs.flat.recommended.languageOptions,
globals: {
...globals.serviceworker,
},
},
},
{
plugins: {
"@next/next": pluginNext,
},
rules: {
...pluginNext.configs.recommended.rules,
...pluginNext.configs["core-web-vitals"].rules,
},
},
{
plugins: {
"react-hooks": pluginReactHooks,
},
settings: { react: { version: "detect" } },
rules: {
...pluginReactHooks.configs.recommended.rules,
// React scope no longer necessary with new JSX transform.
"react/react-in-jsx-scope": "off",
},
},
];

View file

@ -0,0 +1,26 @@
{
"name": "@repo/eslint-config",
"version": "0.0.0",
"type": "module",
"private": true,
"exports": {
".": "./index.js",
"./next": "./next.js",
"./react": "./react.js",
"./remix": "./remix.js",
"./vite": "./vite.js"
},
"devDependencies": {
"@eslint/js": "^9.29.0",
"@next/eslint-plugin-next": "^15.3.0",
"eslint": "^9.29.0",
"eslint-config-prettier": "^10.1.1",
"eslint-plugin-only-warn": "^1.1.0",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-turbo": "^2.5.0",
"globals": "^16.2.0",
"typescript": "^5.8.2",
"typescript-eslint": "^8.34.0"
}
}

39
packages/config-eslint/react.js vendored Normal file
View file

@ -0,0 +1,39 @@
import js from "@eslint/js";
import eslintConfigPrettier from "eslint-config-prettier";
import tseslint from "typescript-eslint";
import pluginReactHooks from "eslint-plugin-react-hooks";
import pluginReact from "eslint-plugin-react";
import globals from "globals";
import { config as baseConfig } from "./index.js";
/**
* A custom ESLint configuration for libraries that use React.
*
* @type {import("eslint").Linter.Config} */
export const config = [
...baseConfig,
js.configs.recommended,
eslintConfigPrettier,
...tseslint.configs.recommended,
pluginReact.configs.flat.recommended,
{
languageOptions: {
...pluginReact.configs.flat.recommended.languageOptions,
globals: {
...globals.serviceworker,
...globals.browser,
},
},
},
{
plugins: {
"react-hooks": pluginReactHooks,
},
settings: { react: { version: "detect" } },
rules: {
...pluginReactHooks.configs.recommended.rules,
// React scope no longer necessary with new JSX transform.
"react/react-in-jsx-scope": "off",
},
},
];

View file

@ -0,0 +1,39 @@
const { resolve } = require("node:path");
const project = resolve(process.cwd(), "tsconfig.json");
/*
* This is a custom ESLint configuration for use a library
* that utilizes React.
*/
module.exports = {
extends: [
"@vercel/style-guide/eslint/browser",
"@vercel/style-guide/eslint/typescript",
"@vercel/style-guide/eslint/react",
].map(require.resolve),
parserOptions: {
project,
},
env: {
browser: true,
node: true,
},
plugins: ["only-warn"],
globals: {
JSX: true,
},
settings: {
"import/resolver": {
typescript: {
project,
},
},
},
ignorePatterns: ["node_modules/", "dist/", ".eslintrc.js", "**/*.css"],
// add rules configurations here
rules: {
"import/no-default-export": "off",
},
};

View file

@ -0,0 +1,26 @@
import js from "@eslint/js";
import globals from "globals";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import tseslint from "typescript-eslint";
export default tseslint.config({
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ["**/*.{ts,tsx}"],
ignores: ["dist"],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
"react-hooks": reactHooks,
"react-refresh": reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
"react-refresh/only-export-components": [
"warn",
{ allowConstantExport: true },
],
},
});

View file

@ -0,0 +1,23 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"composite": false,
"declaration": true,
"declarationMap": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"allowImportingTsExtensions": true,
"inlineSources": false,
"isolatedModules": true,
"module": "ESNext",
"moduleResolution": "Bundler",
"noUnusedLocals": false,
"noUnusedParameters": false,
"preserveWatchOutput": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"strictNullChecks": true
},
"exclude": ["node_modules"]
}

View file

@ -0,0 +1,17 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "./base.json",
"compilerOptions": {
"plugins": [{ "name": "next" }],
"allowJs": true,
"declaration": false,
"declarationMap": false,
"incremental": true,
"jsx": "preserve",
"lib": ["dom", "dom.iterable", "esnext"],
"module": "esnext",
"noEmit": true,
"resolveJsonModule": true,
"target": "es5"
}
}

View file

@ -0,0 +1,9 @@
{
"name": "@repo/typescript-config",
"version": "0.0.0",
"private": true,
"license": "MIT",
"publishConfig": {
"access": "public"
}
}

View file

@ -0,0 +1,16 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "./base.json",
"compilerOptions": {
"allowJs": true,
"declaration": false,
"declarationMap": false,
"incremental": true,
"jsx": "preserve",
"lib": ["dom", "dom.iterable", "esnext"],
"module": "esnext",
"noEmit": true,
"resolveJsonModule": true,
"target": "es5"
}
}

View file

@ -0,0 +1,11 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "./base.json",
"compilerOptions": {
"lib": ["ES2015"],
"module": "ESNext",
"target": "ES6",
"jsx": "react-jsx",
"noEmit": true
}
}

View file

@ -0,0 +1,17 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "./base.json",
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2019"],
"isolatedModules": true,
"esModuleInterop": true,
"jsx": "react-jsx",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"target": "ES2019",
"strict": true,
"allowJs": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": "."
}
}

View file

@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}

View file

@ -0,0 +1,17 @@
/** @type {import('jest').Config} */
const config = {
roots: ["<rootDir>"],
testEnvironment: "jsdom",
transform: {
"^.+\\.tsx?$": "ts-jest",
},
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
modulePathIgnorePatterns: [
"<rootDir>/test/__fixtures__",
"<rootDir>/node_modules",
"<rootDir>/dist",
],
preset: "ts-jest",
};
export default config;

View file

@ -0,0 +1,16 @@
/** @type {import('jest').Config} */
const config = {
roots: ["<rootDir>"],
transform: {
"^.+\\.tsx?$": "ts-jest",
},
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
modulePathIgnorePatterns: [
"<rootDir>/test/__fixtures__",
"<rootDir>/node_modules",
"<rootDir>/dist",
],
preset: "ts-jest",
};
export default config;

View file

@ -0,0 +1,17 @@
{
"name": "@repo/jest-presets",
"version": "0.0.0",
"private": true,
"license": "MIT",
"files": [
"browser/jest-preset.ts",
"node/jest-preset.ts"
],
"dependencies": {
"jest": "^29.7.0",
"ts-jest": "^29.4.0"
},
"devDependencies": {
"jest-environment-jsdom": "^29.7.0"
}
}

View file

@ -0,0 +1,4 @@
import { config } from "@repo/eslint-config";
/** @type {import("eslint").Linter.Config} */
export default config;

View file

@ -0,0 +1,45 @@
{
"name": "@repo/logger",
"version": "0.0.0",
"type": "module",
"private": true,
"files": [
"dist"
],
"main": "./dist/es/index.js",
"module": "./dist/es/index.js",
"types": "./dist/es/index.d.ts",
"exports": {
".": {
"import": {
"types": "./dist/es/index.d.ts",
"default": "./dist/es/index.js"
},
"require": {
"types": "./dist/cjs/index.d.cts",
"default": "./dist/cjs/index.cjs"
}
}
},
"scripts": {
"build": "bunchee",
"dev": "bunchee --watch",
"lint": "eslint src/",
"check-types": "tsc --noEmit",
"test": "jest"
},
"jest": {
"preset": "@repo/jest-presets/node"
},
"devDependencies": {
"@jest/globals": "^29.7.0",
"@repo/eslint-config": "workspace:*",
"@repo/jest-presets": "workspace:*",
"@repo/typescript-config": "workspace:*",
"@types/node": "^22.15.3",
"bunchee": "^6.4.0",
"eslint": "^9.29.0",
"jest": "^29.7.0",
"typescript": "5.8.2"
}
}

View file

@ -0,0 +1,12 @@
import { describe, it, expect, jest } from "@jest/globals";
import { log } from "..";
jest.spyOn(global.console, "log");
describe("@repo/logger", () => {
it("prints a message", () => {
log("hello");
// eslint-disable-next-line no-console -- testing console
expect(console.log).toBeCalledWith("LOGGER: ", "hello");
});
});

View file

@ -0,0 +1,4 @@
export const log = (...args: unknown[]): void => {
// eslint-disable-next-line no-console -- logger
console.log("LOGGER: ", ...args);
};

View file

@ -0,0 +1,10 @@
{
"extends": "@repo/typescript-config/base.json",
"compilerOptions": {
"lib": ["ES2015"],
"outDir": "./dist",
"types": ["jest", "node"]
},
"include": ["."],
"exclude": ["node_modules", "dist"],
}

View file

@ -0,0 +1,12 @@
{
"extends": [
"//"
],
"tasks": {
"build": {
"outputs": [
"dist/**"
]
}
}
}

View file

@ -0,0 +1,4 @@
import { config } from "@repo/eslint-config/react";
/** @type {import("eslint").Linter.Config} */
export default config;

60
packages/ui/package.json Normal file
View file

@ -0,0 +1,60 @@
{
"name": "@repo/ui",
"version": "0.0.0",
"private": true,
"license": "MIT",
"sideEffects": false,
"files": [
"dist/**",
"dist"
],
"exports": {
"./counter-button": {
"import": {
"types": "./dist/es/counter-button.d.mts",
"default": "./dist/es/counter-button.mjs"
},
"require": {
"types": "./dist/cjs/counter-button.d.ts",
"default": "./dist/cjs/counter-button.js"
}
},
"./link": {
"import": {
"types": "./dist/es/link.d.mts",
"default": "./dist/es/link.mjs"
},
"require": {
"types": "./dist/cjs/link.d.ts",
"default": "./dist/cjs/link.js"
}
}
},
"scripts": {
"build": "bunchee",
"dev": "bunchee --watch",
"check-types": "tsc --noEmit",
"lint": "eslint src/",
"test": "jest"
},
"jest": {
"preset": "@repo/jest-presets/browser"
},
"devDependencies": {
"@jest/globals": "^29.7.0",
"@repo/eslint-config": "workspace:*",
"@repo/jest-presets": "workspace:*",
"@repo/typescript-config": "workspace:*",
"@types/node": "^22.15.3",
"bunchee": "^6.4.0",
"eslint": "^9.29.0",
"jest": "^29.7.0",
"typescript": "5.8.2"
},
"peerDependencies": {
"@types/react": ">=18",
"@types/react-dom": ">=18",
"react": ">=18",
"react-dom": ">=18"
}
}

View file

@ -0,0 +1,12 @@
import { describe, it } from "@jest/globals";
import { createRoot } from "react-dom/client";
import { CounterButton } from ".";
describe("CounterButton", () => {
it("renders without crashing", () => {
const div = document.createElement("div");
const root = createRoot(div);
root.render(<CounterButton />);
root.unmount();
});
});

View file

@ -0,0 +1,50 @@
"use client";
import { useState } from "react";
export function CounterButton() {
const [count, setCount] = useState(0);
return (
<div
style={{
background: `rgba(0,0,0,0.05)`,
borderRadius: `8px`,
padding: "1.5rem",
fontWeight: 500,
}}
>
<p style={{ margin: "0 0 1.5rem 0" }}>
This component is from{" "}
<code
style={{
padding: "0.2rem 0.3rem",
background: `rgba(0,0,0,0.1)`,
borderRadius: "0.25rem",
}}
>
ui
</code>
</p>
<div>
<button
onClick={() => {
setCount((c) => c + 1);
}}
style={{
background: "black",
color: "white",
border: "none",
padding: "0.5rem 1rem",
borderRadius: "0.25rem",
display: "inline-block",
cursor: "pointer",
}}
type="button"
>
Count: {count}
</button>
</div>
</div>
);
}

View file

@ -0,0 +1,12 @@
import { describe, it } from "@jest/globals";
import { createRoot } from "react-dom/client";
import { Link } from ".";
describe("Link", () => {
it("renders without crashing", () => {
const div = document.createElement("div");
const root = createRoot(div);
root.render(<Link href="https://turborepo.com">Turborepo Docs</Link>);
root.unmount();
});
});

View file

@ -0,0 +1,20 @@
import type { AnchorHTMLAttributes, ReactNode } from "react";
interface LinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
children: ReactNode;
newTab?: boolean;
href: string;
}
export function Link({ children, href, newTab, ...other }: LinkProps) {
return (
<a
href={href}
rel={newTab ? "noreferrer" : undefined}
target={newTab ? "_blank" : undefined}
{...other}
>
{children}
</a>
);
}

10
packages/ui/tsconfig.json Normal file
View file

@ -0,0 +1,10 @@
{
"extends": "@repo/typescript-config/react-library.json",
"compilerOptions": {
"lib": ["dom", "ES2015"],
"sourceMap": true,
"types": ["jest", "node"]
},
"include": ["."],
"exclude": ["dist", "build", "node_modules"]
}

8
packages/ui/turbo.json Normal file
View file

@ -0,0 +1,8 @@
{
"extends": ["//"],
"tasks": {
"build": {
"outputs": ["dist/**"]
}
}
}