mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-07-02 08:58:36 +00:00
feat(cli): update framework info handling
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -141,11 +141,10 @@ function ModelItem({ model, isSelected, onSelect, onPeek }: ModelItemProps) {
|
||||
mutation.attributeName === "aria-selected" &&
|
||||
ref.current?.getAttribute("aria-selected") === "true"
|
||||
) {
|
||||
onPeek(model);
|
||||
onPeek(model)
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
return (
|
||||
<CommandItem
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
[
|
||||
{
|
||||
"name": "new-york",
|
||||
"label": "New York (Recommended)"
|
||||
},
|
||||
{
|
||||
"name": "default",
|
||||
"label": "Default"
|
||||
},
|
||||
{
|
||||
"name": "new-york",
|
||||
"label": "New York"
|
||||
}
|
||||
]
|
||||
@@ -1,12 +1,12 @@
|
||||
export const styles = [
|
||||
{
|
||||
name: "new-york",
|
||||
label: "New York (Recommended)",
|
||||
},
|
||||
{
|
||||
name: "default",
|
||||
label: "Default",
|
||||
},
|
||||
{
|
||||
name: "new-york",
|
||||
label: "New York",
|
||||
},
|
||||
] as const
|
||||
|
||||
export type Style = (typeof styles)[number]
|
||||
|
||||
@@ -57,11 +57,12 @@ export const add = new Command()
|
||||
|
||||
const config = await getConfig(cwd)
|
||||
if (!config) {
|
||||
logger.warn(
|
||||
logger.error(
|
||||
`Configuration is missing. Please run ${chalk.green(
|
||||
`init`
|
||||
)} to create a components.json file.`
|
||||
)
|
||||
logger.error("")
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { existsSync, promises as fs } from "fs"
|
||||
import path from "path"
|
||||
import * as ERRORS from "@/src/utils/errors"
|
||||
import {
|
||||
DEFAULT_COMPONENTS,
|
||||
DEFAULT_TAILWIND_CONFIG,
|
||||
@@ -11,9 +12,10 @@ import {
|
||||
type Config,
|
||||
} from "@/src/utils/get-config"
|
||||
import { getPackageManager } from "@/src/utils/get-package-manager"
|
||||
import { getProjectConfig, preFlight } from "@/src/utils/get-project-info"
|
||||
import { getProjectConfig } from "@/src/utils/get-project-info"
|
||||
import { handleError } from "@/src/utils/handle-error"
|
||||
import { logger } from "@/src/utils/logger"
|
||||
import { preFlight } from "@/src/utils/preflight"
|
||||
import {
|
||||
getRegistryBaseColor,
|
||||
getRegistryBaseColors,
|
||||
@@ -57,29 +59,65 @@ export const init = new Command()
|
||||
try {
|
||||
const options = initOptionsSchema.parse(opts)
|
||||
const cwd = path.resolve(options.cwd)
|
||||
const preflightResult = await preFlight(cwd)
|
||||
|
||||
// Ensure target directory exists.
|
||||
if (!existsSync(cwd)) {
|
||||
logger.error(`The path ${cwd} does not exist. Please try again.`)
|
||||
if (preflightResult.error === ERRORS.MISSING_DIR) {
|
||||
logger.error(
|
||||
`The path ${cwd} does not exist. Make sure it exists and try again.`
|
||||
)
|
||||
logger.error("")
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
preFlight(cwd)
|
||||
if (preflightResult.error === ERRORS.EXISTING_CONFIG) {
|
||||
logger.error(`The path ${cwd} already contains a components.json file.`)
|
||||
logger.error(
|
||||
"To start over, remove the components.json file and try again."
|
||||
)
|
||||
logger.error("")
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (preflightResult.error === ERRORS.TAILWIND_NOT_CONFIGURED) {
|
||||
const framework =
|
||||
preflightResult.info?.framework &&
|
||||
["next-app", "next-pages"].includes(preflightResult.info?.framework)
|
||||
? "nextjs"
|
||||
: preflightResult.info?.framework
|
||||
const tailwindInstallationUrl = framework
|
||||
? `https://tailwindcss.com/docs/guides/${framework}`
|
||||
: "https://tailwindcss.com/docs/installation/framework-guides"
|
||||
|
||||
logger.error(
|
||||
"Tailwind CSS is not configured. Install Tailwind CSS then run init again.\n" +
|
||||
`Visit ${tailwindInstallationUrl} to get started.\n`
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (preflightResult.error === ERRORS.IMPORT_ALIAS_MISSING) {
|
||||
const framework =
|
||||
preflightResult.info?.framework &&
|
||||
["next-app", "next-pages"].includes(preflightResult.info?.framework)
|
||||
? "next"
|
||||
: preflightResult.info?.framework
|
||||
logger.error(
|
||||
`No import alias found in your tsconfig.json file. \nVisit ${chalk.cyan(
|
||||
`https://ui.shadcn.com/docs/installation/${framework}`
|
||||
)} to learn how to set an import alias.`
|
||||
)
|
||||
logger.error("")
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const projectConfig = await getProjectConfig(cwd)
|
||||
if (projectConfig) {
|
||||
const config = await promptForMinimalConfig(
|
||||
cwd,
|
||||
projectConfig,
|
||||
opts.defaults
|
||||
)
|
||||
await runInit(cwd, config)
|
||||
} else {
|
||||
// Read config.
|
||||
const existingConfig = await getConfig(cwd)
|
||||
const config = await promptForConfig(cwd, existingConfig, options.yes)
|
||||
await runInit(cwd, config)
|
||||
}
|
||||
const config = projectConfig
|
||||
? // If we can determine the project config, prompt for minimal config.
|
||||
await promptForMinimalConfig(cwd, projectConfig, opts.defaults)
|
||||
: // Otherwise, prompt for full config.
|
||||
await promptForConfig(cwd, await getConfig(cwd), options.yes)
|
||||
|
||||
await runInit(cwd, config)
|
||||
|
||||
logger.info("")
|
||||
logger.info(
|
||||
@@ -100,8 +138,10 @@ export async function promptForConfig(
|
||||
) {
|
||||
const highlight = (text: string) => chalk.cyan(text)
|
||||
|
||||
const styles = await getRegistryStyles()
|
||||
const baseColors = await getRegistryBaseColors()
|
||||
const [styles, baseColors] = await Promise.all([
|
||||
getRegistryStyles(),
|
||||
getRegistryBaseColors(),
|
||||
])
|
||||
|
||||
const options = await prompts([
|
||||
{
|
||||
@@ -240,8 +280,10 @@ export async function promptForMinimalConfig(
|
||||
let cssVariables = defaultConfig.tailwind.cssVariables
|
||||
|
||||
if (!defaults) {
|
||||
const styles = await getRegistryStyles()
|
||||
const baseColors = await getRegistryBaseColors()
|
||||
const [styles, baseColors] = await Promise.all([
|
||||
getRegistryStyles(),
|
||||
getRegistryBaseColors(),
|
||||
])
|
||||
|
||||
const options = await prompts([
|
||||
{
|
||||
@@ -252,6 +294,7 @@ export async function promptForMinimalConfig(
|
||||
title: style.label,
|
||||
value: style.name,
|
||||
})),
|
||||
initial: styles.findIndex((s) => s.name === style),
|
||||
},
|
||||
{
|
||||
type: "select",
|
||||
|
||||
5
packages/cli/src/utils/errors.ts
Normal file
5
packages/cli/src/utils/errors.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export const MISSING_DIR = "0"
|
||||
export const EXISTING_CONFIG = "1"
|
||||
export const EMPTY_PROJECT = "2"
|
||||
export const TAILWIND_NOT_CONFIGURED = "3"
|
||||
export const IMPORT_ALIAS_MISSING = "4"
|
||||
@@ -1,4 +1,3 @@
|
||||
import { existsSync } from "fs"
|
||||
import path from "path"
|
||||
import {
|
||||
Config,
|
||||
@@ -10,20 +9,20 @@ import fg from "fast-glob"
|
||||
import fs, { pathExists } from "fs-extra"
|
||||
import { loadConfig } from "tsconfig-paths"
|
||||
|
||||
// TODO: Add support for more frameworks.
|
||||
// We'll start with Next.js for now.
|
||||
const PROJECT_TYPES = [
|
||||
const SUPPORTED_FRAMEWORKS = [
|
||||
"next-app",
|
||||
"next-pages",
|
||||
"remix",
|
||||
"astro",
|
||||
"vite",
|
||||
] as const
|
||||
|
||||
type ProjectType = {
|
||||
framework: (typeof PROJECT_TYPES)[number]
|
||||
type ProjectInfo = {
|
||||
framework: (typeof SUPPORTED_FRAMEWORKS)[number]
|
||||
isUsingSrcDir: boolean
|
||||
isTypescript: boolean
|
||||
tailwindConfigFile: string | null
|
||||
tailwindCssFile: string | null
|
||||
tsConfigAliasPrefix: string | null
|
||||
}
|
||||
|
||||
const PROJECT_SHARED_IGNORE = [
|
||||
@@ -34,61 +33,15 @@ const PROJECT_SHARED_IGNORE = [
|
||||
"build",
|
||||
]
|
||||
|
||||
export async function getTsConfig() {
|
||||
try {
|
||||
const tsconfigPath = path.join("tsconfig.json")
|
||||
const tsconfig = await fs.readJSON(tsconfigPath)
|
||||
|
||||
if (!tsconfig) {
|
||||
throw new Error("tsconfig.json is missing")
|
||||
}
|
||||
|
||||
return tsconfig
|
||||
} catch (error) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export async function getProjectConfig(cwd: string): Promise<Config | null> {
|
||||
// Check for existing component config.
|
||||
const existingConfig = await getConfig(cwd)
|
||||
if (existingConfig) {
|
||||
return existingConfig
|
||||
}
|
||||
|
||||
const projectType = await getProjectType(cwd)
|
||||
const tailwindCssFile = await getTailwindCssFile(cwd)
|
||||
const tsConfigAliasPrefix = await getTsConfigAliasPrefix(cwd)
|
||||
|
||||
if (!projectType || !tailwindCssFile || !tsConfigAliasPrefix) {
|
||||
return null
|
||||
}
|
||||
|
||||
const isTsx = await isTypeScriptProject(cwd)
|
||||
|
||||
const config: RawConfig = {
|
||||
$schema: "https://ui.shadcn.com/schema.json",
|
||||
rsc: ["next-app", "next-app-src"].includes(projectType.framework),
|
||||
tsx: isTsx,
|
||||
style: "new-york",
|
||||
tailwind: {
|
||||
config: isTsx ? "tailwind.config.ts" : "tailwind.config.js",
|
||||
baseColor: "zinc",
|
||||
css: tailwindCssFile,
|
||||
cssVariables: true,
|
||||
prefix: "",
|
||||
},
|
||||
aliases: {
|
||||
utils: `${tsConfigAliasPrefix}/lib/utils`,
|
||||
components: `${tsConfigAliasPrefix}/components`,
|
||||
},
|
||||
}
|
||||
|
||||
return await resolveConfigPaths(cwd, config)
|
||||
}
|
||||
|
||||
export async function getProjectType(cwd: string): Promise<ProjectType | null> {
|
||||
const [configFiles, isUsingSrcDir, isTypescript] = await Promise.all([
|
||||
export async function getProjectInfo(cwd: string): Promise<ProjectInfo | null> {
|
||||
const [
|
||||
configFiles,
|
||||
isUsingSrcDir,
|
||||
isTypescript,
|
||||
tailwindConfigFile,
|
||||
tailwindCssFile,
|
||||
tsConfigAliasPrefix,
|
||||
] = await Promise.all([
|
||||
fg.glob("**/{next,vite,astro}.config.*", {
|
||||
cwd,
|
||||
deep: 3,
|
||||
@@ -96,7 +49,11 @@ export async function getProjectType(cwd: string): Promise<ProjectType | null> {
|
||||
}),
|
||||
fs.pathExists(path.resolve(cwd, "src")),
|
||||
isTypeScriptProject(cwd),
|
||||
getTailwindConfigFile(cwd),
|
||||
getTailwindCssFile(cwd),
|
||||
getTsConfigAliasPrefix(cwd),
|
||||
])
|
||||
|
||||
const isUsingAppDir = await fs.pathExists(
|
||||
path.resolve(cwd, `${isUsingSrcDir ? "src/" : ""}app`)
|
||||
)
|
||||
@@ -105,10 +62,13 @@ export async function getProjectType(cwd: string): Promise<ProjectType | null> {
|
||||
return null
|
||||
}
|
||||
|
||||
const type: ProjectType = {
|
||||
const type: ProjectInfo = {
|
||||
framework: "next-app",
|
||||
isUsingSrcDir,
|
||||
isTypescript,
|
||||
tailwindConfigFile,
|
||||
tailwindCssFile,
|
||||
tsConfigAliasPrefix,
|
||||
}
|
||||
|
||||
// Next.js.
|
||||
@@ -117,13 +77,6 @@ export async function getProjectType(cwd: string): Promise<ProjectType | null> {
|
||||
return type
|
||||
}
|
||||
|
||||
// Astro.
|
||||
if (configFiles.find((file) => file.startsWith("astro.config."))?.length) {
|
||||
type.framework = "astro"
|
||||
type.isTypescript = true
|
||||
return type
|
||||
}
|
||||
|
||||
// Vite and Remix.
|
||||
// They both have a vite.config.* file.
|
||||
if (configFiles.find((file) => file.startsWith("vite.config."))?.length) {
|
||||
@@ -159,6 +112,20 @@ export async function getTailwindCssFile(cwd: string) {
|
||||
return null
|
||||
}
|
||||
|
||||
export async function getTailwindConfigFile(cwd: string) {
|
||||
const files = await fg.glob("tailwind.config.*", {
|
||||
cwd,
|
||||
deep: 3,
|
||||
ignore: PROJECT_SHARED_IGNORE,
|
||||
})
|
||||
|
||||
if (!files.length) {
|
||||
return null
|
||||
}
|
||||
|
||||
return files[0]
|
||||
}
|
||||
|
||||
export async function getTsConfigAliasPrefix(cwd: string) {
|
||||
const tsConfig = await loadConfig(cwd)
|
||||
|
||||
@@ -168,8 +135,12 @@ export async function getTsConfigAliasPrefix(cwd: string) {
|
||||
|
||||
// This assume that the first alias is the prefix.
|
||||
for (const [alias, paths] of Object.entries(tsConfig.paths)) {
|
||||
if (paths.includes("./*") || paths.includes("./src/*")) {
|
||||
return alias.at(0)
|
||||
if (
|
||||
paths.includes("./*") ||
|
||||
paths.includes("./src/*") ||
|
||||
paths.includes("./app/*")
|
||||
) {
|
||||
return alias.at(0) ?? null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,23 +148,66 @@ export async function getTsConfigAliasPrefix(cwd: string) {
|
||||
}
|
||||
|
||||
export async function isTypeScriptProject(cwd: string) {
|
||||
// Check if cwd has a tsconfig.json file.
|
||||
return pathExists(path.resolve(cwd, "tsconfig.json"))
|
||||
}
|
||||
|
||||
export async function preFlight(cwd: string) {
|
||||
// We need Tailwind CSS to be configured.
|
||||
const tailwindConfig = await fg.glob("tailwind.config.*", {
|
||||
const files = await fg.glob("tsconfig.*", {
|
||||
cwd,
|
||||
deep: 3,
|
||||
deep: 1,
|
||||
ignore: PROJECT_SHARED_IGNORE,
|
||||
})
|
||||
|
||||
if (!tailwindConfig.length) {
|
||||
throw new Error(
|
||||
"Tailwind CSS is not installed. Visit https://tailwindcss.com/docs/installation to get started."
|
||||
)
|
||||
return files.length > 0
|
||||
}
|
||||
|
||||
export async function getTsConfig() {
|
||||
try {
|
||||
const tsconfigPath = path.join("tsconfig.json")
|
||||
const tsconfig = await fs.readJSON(tsconfigPath)
|
||||
|
||||
if (!tsconfig) {
|
||||
throw new Error("tsconfig.json is missing")
|
||||
}
|
||||
|
||||
return tsconfig
|
||||
} catch (error) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export async function getProjectConfig(cwd: string): Promise<Config | null> {
|
||||
// Check for existing component config.
|
||||
const [existingConfig, projectInfo] = await Promise.all([
|
||||
getConfig(cwd),
|
||||
getProjectInfo(cwd),
|
||||
])
|
||||
|
||||
if (existingConfig) {
|
||||
return existingConfig
|
||||
}
|
||||
|
||||
return true
|
||||
if (
|
||||
!projectInfo ||
|
||||
!projectInfo.tailwindConfigFile ||
|
||||
!projectInfo.tailwindCssFile
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
const config: RawConfig = {
|
||||
$schema: "https://ui.shadcn.com/schema.json",
|
||||
rsc: ["next-app", "next-app-src"].includes(projectInfo.framework),
|
||||
tsx: projectInfo.isTypescript,
|
||||
style: "new-york",
|
||||
tailwind: {
|
||||
config: projectInfo.tailwindConfigFile,
|
||||
baseColor: "zinc",
|
||||
css: projectInfo.tailwindCssFile,
|
||||
cssVariables: true,
|
||||
prefix: "",
|
||||
},
|
||||
aliases: {
|
||||
utils: `${projectInfo.tsConfigAliasPrefix}/lib/utils`,
|
||||
components: `${projectInfo.tsConfigAliasPrefix}/components`,
|
||||
},
|
||||
}
|
||||
|
||||
return await resolveConfigPaths(cwd, config)
|
||||
}
|
||||
|
||||
51
packages/cli/src/utils/preflight.ts
Normal file
51
packages/cli/src/utils/preflight.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import path from "path"
|
||||
import * as ERRORS from "@/src/utils/errors"
|
||||
import { getProjectInfo } from "@/src/utils/get-project-info"
|
||||
import fs from "fs-extra"
|
||||
|
||||
export async function preFlight(cwd: string) {
|
||||
// Ensure target directory exists.
|
||||
if (!fs.existsSync(cwd)) {
|
||||
return {
|
||||
error: ERRORS.MISSING_DIR,
|
||||
info: null,
|
||||
}
|
||||
}
|
||||
|
||||
// Check for existing components.json file.
|
||||
if (fs.existsSync(path.resolve(cwd, "components.json"))) {
|
||||
return {
|
||||
error: ERRORS.EXISTING_CONFIG,
|
||||
info: null,
|
||||
}
|
||||
}
|
||||
|
||||
// Check for empty project. We assume if no package.json exists, the project is empty.
|
||||
if (!fs.existsSync(path.resolve(cwd, "package.json"))) {
|
||||
return {
|
||||
error: ERRORS.EMPTY_PROJECT,
|
||||
info: null,
|
||||
}
|
||||
}
|
||||
|
||||
const projectInfo = await getProjectInfo(cwd)
|
||||
|
||||
if (!projectInfo?.tailwindConfigFile || !projectInfo?.tailwindCssFile) {
|
||||
return {
|
||||
error: ERRORS.TAILWIND_NOT_CONFIGURED,
|
||||
info: projectInfo,
|
||||
}
|
||||
}
|
||||
|
||||
if (!projectInfo.tsConfigAliasPrefix) {
|
||||
return {
|
||||
error: ERRORS.IMPORT_ALIAS_MISSING,
|
||||
info: projectInfo,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
error: null,
|
||||
info: projectInfo,
|
||||
}
|
||||
}
|
||||
@@ -39,8 +39,8 @@ export async function getRegistryStyles() {
|
||||
export async function getRegistryBaseColors() {
|
||||
return [
|
||||
{
|
||||
name: "slate",
|
||||
label: "Slate",
|
||||
name: "neutral",
|
||||
label: "Neutral",
|
||||
},
|
||||
{
|
||||
name: "gray",
|
||||
@@ -50,14 +50,14 @@ export async function getRegistryBaseColors() {
|
||||
name: "zinc",
|
||||
label: "Zinc",
|
||||
},
|
||||
{
|
||||
name: "neutral",
|
||||
label: "Neutral",
|
||||
},
|
||||
{
|
||||
name: "stone",
|
||||
label: "Stone",
|
||||
},
|
||||
{
|
||||
name: "slate",
|
||||
label: "Slate",
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
import { defineConfig } from 'astro/config';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({});
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"name": "test-cli-astro",
|
||||
"type": "module",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro check && astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"astro": "^4.13.3",
|
||||
"@astrojs/check": "^0.9.2",
|
||||
"typescript": "^5.5.4"
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
|
||||
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
|
||||
<style>
|
||||
path { fill: #000; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
path { fill: #FFF; }
|
||||
}
|
||||
</style>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 749 B |
@@ -1 +0,0 @@
|
||||
/// <reference types="astro/client" />
|
||||
@@ -1,50 +0,0 @@
|
||||
---
|
||||
interface Props {
|
||||
title: string;
|
||||
}
|
||||
|
||||
const { title } = Astro.props;
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="description" content="Astro description" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<title>{title}</title>
|
||||
</head>
|
||||
<body>
|
||||
<slot />
|
||||
</body>
|
||||
</html>
|
||||
<style is:global>
|
||||
:root {
|
||||
--accent: 136, 58, 234;
|
||||
--accent-light: 224, 204, 250;
|
||||
--accent-dark: 49, 10, 101;
|
||||
--accent-gradient: linear-gradient(
|
||||
45deg,
|
||||
rgb(var(--accent)),
|
||||
rgb(var(--accent-light)) 30%,
|
||||
white 60%
|
||||
);
|
||||
}
|
||||
html {
|
||||
font-family: system-ui, sans-serif;
|
||||
background: #13151a;
|
||||
}
|
||||
code {
|
||||
font-family:
|
||||
Menlo,
|
||||
Monaco,
|
||||
Lucida Console,
|
||||
Liberation Mono,
|
||||
DejaVu Sans Mono,
|
||||
Bitstream Vera Sans Mono,
|
||||
Courier New,
|
||||
monospace;
|
||||
}
|
||||
</style>
|
||||
@@ -1,123 +0,0 @@
|
||||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
import Card from '../components/Card.astro';
|
||||
---
|
||||
|
||||
<Layout title="Welcome to Astro.">
|
||||
<main>
|
||||
<svg
|
||||
class="astro-a"
|
||||
width="495"
|
||||
height="623"
|
||||
viewBox="0 0 495 623"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M167.19 364.254C83.4786 364.254 0 404.819 0 404.819C0 404.819 141.781 19.4876 142.087 18.7291C146.434 7.33701 153.027 0 162.289 0H332.441C341.703 0 348.574 7.33701 352.643 18.7291C352.92 19.5022 494.716 404.819 494.716 404.819C494.716 404.819 426.67 364.254 327.525 364.254L264.41 169.408C262.047 159.985 255.147 153.581 247.358 153.581C239.569 153.581 232.669 159.985 230.306 169.408L167.19 364.254ZM160.869 530.172C160.877 530.18 160.885 530.187 160.894 530.195L160.867 530.181C160.868 530.178 160.868 530.175 160.869 530.172ZM136.218 411.348C124.476 450.467 132.698 504.458 160.869 530.172C160.997 529.696 161.125 529.242 161.248 528.804C161.502 527.907 161.737 527.073 161.917 526.233C165.446 509.895 178.754 499.52 195.577 500.01C211.969 500.487 220.67 508.765 223.202 527.254C224.141 534.12 224.23 541.131 224.319 548.105C224.328 548.834 224.337 549.563 224.347 550.291C224.563 566.098 228.657 580.707 237.264 593.914C245.413 606.426 256.108 615.943 270.749 622.478C270.593 621.952 270.463 621.508 270.35 621.126C270.045 620.086 269.872 619.499 269.685 618.911C258.909 585.935 266.668 563.266 295.344 543.933C298.254 541.971 301.187 540.041 304.12 538.112C310.591 533.854 317.059 529.599 323.279 525.007C345.88 508.329 360.09 486.327 363.431 457.844C364.805 446.148 363.781 434.657 359.848 423.275C358.176 424.287 356.587 425.295 355.042 426.275C351.744 428.366 348.647 430.33 345.382 431.934C303.466 452.507 259.152 455.053 214.03 448.245C184.802 443.834 156.584 436.019 136.218 411.348Z"
|
||||
fill="url(#paint0_linear_1805_24383)"></path>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="paint0_linear_1805_24383"
|
||||
x1="247.358"
|
||||
y1="0"
|
||||
x2="247.358"
|
||||
y2="622.479"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-opacity="0.9"></stop>
|
||||
<stop offset="1" stop-opacity="0.2"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<h1>Welcome to <span class="text-gradient">Astro</span></h1>
|
||||
<p class="instructions">
|
||||
To get started, open the directory <code>src/pages</code> in your project.<br />
|
||||
<strong>Code Challenge:</strong> Tweak the "Welcome to Astro" message above.
|
||||
</p>
|
||||
<ul role="list" class="link-card-grid">
|
||||
<Card
|
||||
href="https://docs.astro.build/"
|
||||
title="Documentation"
|
||||
body="Learn how Astro works and explore the official API docs."
|
||||
/>
|
||||
<Card
|
||||
href="https://astro.build/integrations/"
|
||||
title="Integrations"
|
||||
body="Supercharge your project with new frameworks and libraries."
|
||||
/>
|
||||
<Card
|
||||
href="https://astro.build/themes/"
|
||||
title="Themes"
|
||||
body="Explore a galaxy of community-built starter themes."
|
||||
/>
|
||||
<Card
|
||||
href="https://astro.build/chat/"
|
||||
title="Community"
|
||||
body="Come say hi to our amazing Discord community. ❤️"
|
||||
/>
|
||||
</ul>
|
||||
</main>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
main {
|
||||
margin: auto;
|
||||
padding: 1rem;
|
||||
width: 800px;
|
||||
max-width: calc(100% - 2rem);
|
||||
color: white;
|
||||
font-size: 20px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.astro-a {
|
||||
position: absolute;
|
||||
top: -32px;
|
||||
left: 50%;
|
||||
transform: translatex(-50%);
|
||||
width: 220px;
|
||||
height: auto;
|
||||
z-index: -1;
|
||||
}
|
||||
h1 {
|
||||
font-size: 4rem;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
.text-gradient {
|
||||
background-image: var(--accent-gradient);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-size: 400%;
|
||||
background-position: 0%;
|
||||
}
|
||||
.instructions {
|
||||
margin-bottom: 2rem;
|
||||
border: 1px solid rgba(var(--accent-light), 25%);
|
||||
background: linear-gradient(rgba(var(--accent-dark), 66%), rgba(var(--accent-dark), 33%));
|
||||
padding: 1.5rem;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.instructions code {
|
||||
font-size: 0.8em;
|
||||
font-weight: bold;
|
||||
background: rgba(var(--accent-light), 12%);
|
||||
color: rgb(var(--accent-light));
|
||||
border-radius: 4px;
|
||||
padding: 0.3em 0.4em;
|
||||
}
|
||||
.instructions strong {
|
||||
color: rgb(var(--accent-light));
|
||||
}
|
||||
.link-card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(24ch, 1fr));
|
||||
gap: 2rem;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -1,38 +1,26 @@
|
||||
import js from '@eslint/js'
|
||||
import globals from 'globals'
|
||||
import react from 'eslint-plugin-react'
|
||||
import reactHooks from 'eslint-plugin-react-hooks'
|
||||
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||
import tseslint from 'typescript-eslint'
|
||||
|
||||
export default [
|
||||
{
|
||||
files: ['**/*.{js,jsx}'],
|
||||
ignores: ['dist'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
ecmaFeatures: { jsx: true },
|
||||
sourceType: 'module',
|
||||
},
|
||||
},
|
||||
settings: { react: { version: '18.3' } },
|
||||
plugins: {
|
||||
react,
|
||||
'react-hooks': reactHooks,
|
||||
'react-refresh': reactRefresh,
|
||||
},
|
||||
rules: {
|
||||
...js.configs.recommended.rules,
|
||||
...react.configs.recommended.rules,
|
||||
...react.configs['jsx-runtime'].rules,
|
||||
...reactHooks.configs.recommended.rules,
|
||||
'react/jsx-no-target-blank': 'off',
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
},
|
||||
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 },
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + React</title>
|
||||
<title>Vite + React + TS</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
4084
packages/cli/test/fixtures/frameworks/vite/package-lock.json
generated
vendored
Normal file
4084
packages/cli/test/fixtures/frameworks/vite/package-lock.json
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"build": "tsc -b && vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
@@ -18,11 +18,15 @@
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^9.8.0",
|
||||
"eslint-plugin-react": "^7.35.0",
|
||||
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.9",
|
||||
"globals": "^15.9.0",
|
||||
"postcss": "^8.4.41",
|
||||
"tailwindcss": "^3.4.10",
|
||||
"typescript": "^5.5.3",
|
||||
"typescript-eslint": "^8.0.0",
|
||||
"vite": "^5.4.0"
|
||||
}
|
||||
}
|
||||
|
||||
6
packages/cli/test/fixtures/frameworks/vite/postcss.config.js
vendored
Normal file
6
packages/cli/test/fixtures/frameworks/vite/postcss.config.js
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
@@ -22,7 +22,7 @@ function App() {
|
||||
count is {count}
|
||||
</button>
|
||||
<p>
|
||||
Edit <code>src/App.jsx</code> and save to test HMR
|
||||
Edit <code>src/App.tsx</code> and save to test HMR
|
||||
</p>
|
||||
</div>
|
||||
<p className="read-the-docs">
|
||||
@@ -1,68 +1,3 @@
|
||||
:root {
|
||||
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
|
||||
color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: #242424;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: #646cff;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
a:hover {
|
||||
color: #535bf2;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.6em 1.2em;
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
background-color: #1a1a1a;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.25s;
|
||||
}
|
||||
button:hover {
|
||||
border-color: #646cff;
|
||||
}
|
||||
button:focus,
|
||||
button:focus-visible {
|
||||
outline: 4px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
a:hover {
|
||||
color: #747bff;
|
||||
}
|
||||
button {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
}
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import App from './App.jsx'
|
||||
import App from './App.tsx'
|
||||
import './index.css'
|
||||
|
||||
createRoot(document.getElementById('root')).render(
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
1
packages/cli/test/fixtures/frameworks/vite/src/vite-env.d.ts
vendored
Normal file
1
packages/cli/test/fixtures/frameworks/vite/src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
8
packages/cli/test/fixtures/frameworks/vite/tailwind.config.js
vendored
Normal file
8
packages/cli/test/fixtures/frameworks/vite/tailwind.config.js
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
24
packages/cli/test/fixtures/frameworks/vite/tsconfig.app.json
vendored
Normal file
24
packages/cli/test/fixtures/frameworks/vite/tsconfig.app.json
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
7
packages/cli/test/fixtures/frameworks/vite/tsconfig.json
vendored
Normal file
7
packages/cli/test/fixtures/frameworks/vite/tsconfig.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
}
|
||||
22
packages/cli/test/fixtures/frameworks/vite/tsconfig.node.json
vendored
Normal file
22
packages/cli/test/fixtures/frameworks/vite/tsconfig.node.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2023"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import path from "path"
|
||||
import { describe, expect, test } from "vitest"
|
||||
|
||||
import { getProjectType } from "../../src/utils/get-project-info"
|
||||
import { getProjectInfo } from "../../src/utils/get-project-info"
|
||||
|
||||
describe("get project type", async () => {
|
||||
describe("get project info", async () => {
|
||||
test.each([
|
||||
{
|
||||
name: "next-app",
|
||||
@@ -11,6 +11,9 @@ describe("get project type", async () => {
|
||||
framework: "next-app",
|
||||
isUsingSrcDir: false,
|
||||
isTypescript: true,
|
||||
tailwindConfigFile: "tailwind.config.ts",
|
||||
tailwindCssFile: "app/globals.css",
|
||||
tsConfigAliasPrefix: "@",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -19,6 +22,9 @@ describe("get project type", async () => {
|
||||
framework: "next-app",
|
||||
isUsingSrcDir: true,
|
||||
isTypescript: true,
|
||||
tailwindConfigFile: "tailwind.config.ts",
|
||||
tailwindCssFile: "src/app/styles.css",
|
||||
tsConfigAliasPrefix: "#",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -27,6 +33,9 @@ describe("get project type", async () => {
|
||||
framework: "next-pages",
|
||||
isUsingSrcDir: false,
|
||||
isTypescript: true,
|
||||
tailwindConfigFile: "tailwind.config.ts",
|
||||
tailwindCssFile: "styles/globals.css",
|
||||
tsConfigAliasPrefix: "~",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -35,14 +44,9 @@ describe("get project type", async () => {
|
||||
framework: "next-pages",
|
||||
isUsingSrcDir: true,
|
||||
isTypescript: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "t3-pages",
|
||||
type: {
|
||||
framework: "next-pages",
|
||||
isUsingSrcDir: true,
|
||||
isTypescript: true,
|
||||
tailwindConfigFile: "tailwind.config.ts",
|
||||
tailwindCssFile: "src/styles/globals.css",
|
||||
tsConfigAliasPrefix: "@",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -51,22 +55,20 @@ describe("get project type", async () => {
|
||||
framework: "next-app",
|
||||
isUsingSrcDir: true,
|
||||
isTypescript: true,
|
||||
tailwindConfigFile: "tailwind.config.ts",
|
||||
tailwindCssFile: "src/styles/globals.css",
|
||||
tsConfigAliasPrefix: "~",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "astro",
|
||||
name: "t3-pages",
|
||||
type: {
|
||||
framework: "astro",
|
||||
framework: "next-pages",
|
||||
isUsingSrcDir: true,
|
||||
isTypescript: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "vite",
|
||||
type: {
|
||||
framework: "vite",
|
||||
isUsingSrcDir: true,
|
||||
isTypescript: false,
|
||||
tailwindConfigFile: "tailwind.config.ts",
|
||||
tailwindCssFile: "src/styles/globals.css",
|
||||
tsConfigAliasPrefix: "~",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -75,11 +77,25 @@ describe("get project type", async () => {
|
||||
framework: "remix",
|
||||
isUsingSrcDir: false,
|
||||
isTypescript: true,
|
||||
tailwindConfigFile: "tailwind.config.ts",
|
||||
tailwindCssFile: "app/tailwind.css",
|
||||
tsConfigAliasPrefix: "~",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "vite",
|
||||
type: {
|
||||
framework: "vite",
|
||||
isUsingSrcDir: true,
|
||||
isTypescript: true,
|
||||
tailwindConfigFile: "tailwind.config.js",
|
||||
tailwindCssFile: "src/index.css",
|
||||
tsConfigAliasPrefix: null,
|
||||
},
|
||||
},
|
||||
])(`getProjectType($name) -> $type`, async ({ name, type }) => {
|
||||
expect(
|
||||
await getProjectType(
|
||||
await getProjectInfo(
|
||||
path.resolve(__dirname, `../fixtures/frameworks/${name}`)
|
||||
)
|
||||
).toStrictEqual(type)
|
||||
|
||||
@@ -3,7 +3,7 @@ import { describe, expect, test } from "vitest"
|
||||
|
||||
import { getTailwindCssFile } from "../../src/utils/get-project-info"
|
||||
|
||||
describe("get tailwind css file", async () => {
|
||||
describe("get tailwindcss file", async () => {
|
||||
test.each([
|
||||
{
|
||||
name: "next-app",
|
||||
@@ -25,6 +25,18 @@ describe("get tailwind css file", async () => {
|
||||
name: "t3-app",
|
||||
file: "src/styles/globals.css",
|
||||
},
|
||||
{
|
||||
name: "t3-pages",
|
||||
file: "src/styles/globals.css",
|
||||
},
|
||||
{
|
||||
name: "remix",
|
||||
file: "app/tailwind.css",
|
||||
},
|
||||
{
|
||||
name: "vite",
|
||||
file: "src/index.css",
|
||||
},
|
||||
])(`getTailwindCssFile($name) -> $file`, async ({ name, file }) => {
|
||||
expect(
|
||||
await getTailwindCssFile(
|
||||
|
||||
Reference in New Issue
Block a user