diff --git a/examples/example-nextjs/package.json b/examples/example-nextjs/package.json index bdc49ecfb..4064b4abb 100644 --- a/examples/example-nextjs/package.json +++ b/examples/example-nextjs/package.json @@ -16,6 +16,7 @@ "react-dom": "^19.0.0-rc-de68d2f4-20241204" }, "devDependencies": { + "autoprefixer": "^10.4.20", "@stylexjs/eslint-plugin": "0.17.5", "@stylexjs/postcss-plugin": "0.17.5", "@types/json-schema": "^7.0.15", diff --git a/packages/@stylexjs/create-stylex-app/.babelrc.js b/packages/@stylexjs/create-stylex-app/.babelrc.js new file mode 100644 index 000000000..863993347 --- /dev/null +++ b/packages/@stylexjs/create-stylex-app/.babelrc.js @@ -0,0 +1,24 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +'use strict'; + +module.exports = { + assumptions: { + iterableIsArray: true, + }, + presets: [ + [ + '@babel/preset-env', + { + exclude: ['@babel/plugin-transform-typeof-symbol'], + targets: { node: '18' }, + }, + ], + ], +}; diff --git a/packages/@stylexjs/create-stylex-app/package.json b/packages/@stylexjs/create-stylex-app/package.json new file mode 100644 index 000000000..e19471fe7 --- /dev/null +++ b/packages/@stylexjs/create-stylex-app/package.json @@ -0,0 +1,38 @@ +{ + "name": "@stylexjs/create-stylex-app", + "version": "0.1.0", + "description": "Scaffold a new StyleX project from official templates", + "repository": "https://www.github.com/facebook/stylex", + "license": "MIT", + "private": true, + "bin": { + "create-stylex-app": "./lib/index.js" + }, + "files": [ + "lib" + ], + "scripts": { + "build": "cross-env BABEL_ENV=cjs babel src/ --out-dir lib/ --copy-files", + "test": "echo \"No tests yet\"" + }, + "keywords": [ + "stylex", + "scaffold", + "cli", + "create-app" + ], + "dependencies": { + "fs-extra": "^11.3.0", + "cross-spawn": "^7.0.6", + "@clack/prompts": "^0.11.0", + "picocolors": "^1.1.0", + "giget": "^2.0.0" + }, + "devDependencies": { + "@babel/cli": "^7.26.4", + "@babel/core": "^7.26.8", + "@babel/preset-env": "^7.26.8", + "cross-env": "^10.1.0", + "scripts": "0.17.5" + } +} diff --git a/packages/@stylexjs/create-stylex-app/src/index.js b/packages/@stylexjs/create-stylex-app/src/index.js new file mode 100644 index 000000000..a76e8be5a --- /dev/null +++ b/packages/@stylexjs/create-stylex-app/src/index.js @@ -0,0 +1,426 @@ +#!/usr/bin/env node + +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +'use strict'; + +const fs = require('fs-extra'); +const path = require('path'); +const pc = require('picocolors'); +const p = require('@clack/prompts'); +const { getTemplates, getBundledTemplates } = require('./templates'); + +const PRIMARY = '#5B45DE'; +const SECONDARY = '#D573DD'; + +const hex = (color) => (text) => + `\x1b[38;2;${parseInt(color.slice(1, 3), 16)};${parseInt(color.slice(3, 5), 16)};${parseInt(color.slice(5, 7), 16)}m${text}\x1b[0m`; + +function showWelcomeBanner() { + const primary = hex(PRIMARY); + const secondary = hex(SECONDARY); + console.log( + primary(` +███████╗████████╗██╗ ██╗██╗ ███████╗`) + + secondary('██╗ ██╗') + + ` +` + + primary('██╔════╝╚══██╔══╝╚██╗ ██╔╝██║ ██╔════╝') + + secondary('╚██╗██╔╝') + + ` +` + + primary('███████╗ ██║ ╚████╔╝ ██║ █████╗ ') + + secondary(' ╚███╔╝ ') + + ` +` + + primary('╚════██║ ██║ ╚██╔╝ ██║ ██╔══╝ ') + + secondary(' ██╔██╗ ') + + ` +` + + primary('███████║ ██║ ██║ ███████╗███████╗') + + secondary('██╔╝ ██╗') + + ` +` + + primary('╚══════╝ ╚═╝ ╚═╝ ╚══════╝╚══════╝') + + secondary('╚═╝ ╚═╝'), + ); + console.log(secondary(' Create StyleX App\n')); +} + +const { + fetchTemplate, + fetchCustomTemplate, +} = require('./utils/fetch-template'); +const { + detectPackageManager, + installDependencies, +} = require('./utils/packages'); + +/** + * Parse command line arguments + */ +function parseArgs(args) { + const result = { + projectName: undefined, + framework: undefined, + template: undefined, + install: true, + help: false, + }; + + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + + if (arg === '--help' || arg === '-h') { + result.help = true; + } else if (arg === '--no-install') { + result.install = false; + } else if (arg === '--framework' || arg === '-f') { + result.framework = args[++i]; + } else if (arg === '--template' || arg === '-t') { + result.template = args[++i]; + } else if (!arg.startsWith('-') && !result.projectName) { + result.projectName = arg; + } + } + + return result; +} + +function showHelp() { + const templateIds = getBundledTemplates() + .map((t) => t.id) + .join(', '); + console.log(` +${pc.bold('create-stylex-app')} - Create a new StyleX project + +${pc.bold('Usage:')} + npx create-stylex-app [options] + +${pc.bold('Options:')} + -f, --framework Framework to use + -t, --template Custom template (GitHub URL or github:owner/repo/path) + --no-install Skip dependency installation + -h, --help Show this help message + +${pc.bold('Available frameworks:')} + ${templateIds} + +${pc.bold('Examples:')} + npx create-stylex-app my-app + npx create-stylex-app my-app --framework nextjs + npx create-stylex-app my-app --template github:user/repo/template +`); +} + +async function main() { + const argv = parseArgs(process.argv.slice(2)); + + if (argv.help) { + showHelp(); + process.exit(0); + } + + showWelcomeBanner(); + + p.intro(pc.bgMagenta(pc.white(' create-stylex-app '))); + + const projectName = argv.projectName; + + if (!projectName) { + p.cancel( + 'Project name is required. Usage: npx create-stylex-app ', + ); + process.exit(1); + } + + const validNameRegex = /^[a-z0-9-_]+$/; + if (!validNameRegex.test(projectName)) { + p.cancel( + `Invalid project name: "${projectName}"\n` + + ' Project names can only contain lowercase letters, numbers, hyphens, and underscores.', + ); + process.exit(1); + } + + p.log.success(`Project name: ${pc.cyan(projectName)}`); + + const targetDir = path.resolve(process.cwd(), projectName); + + if (await fs.pathExists(targetDir)) { + p.cancel( + `Directory "${projectName}" already exists.\n` + + ' Choose a different name or remove the existing directory.', + ); + process.exit(1); + } + + p.log.success('Directory available'); + + if (argv.template) { + await handleCustomTemplate(argv, projectName, targetDir); + return; + } + + const templatesSpinner = p.spinner(); + templatesSpinner.start('Fetching available templates...'); + const templates = await getTemplates(); + templatesSpinner.stop(`Found ${templates.length} templates`); + + let templateId = argv.framework; + + if (!templateId) { + templateId = await p.select({ + message: 'Select a framework', + options: templates.map((t) => ({ + value: t.id, + label: t.name + (t.recommended ? pc.yellow(' (recommended)') : ''), + hint: t.description, + })), + }); + + if (p.isCancel(templateId)) { + p.cancel('Operation cancelled.'); + process.exit(0); + } + } + + const template = templates.find((t) => t.id === templateId); + if (!template) { + p.cancel( + `Template "${templateId}" not found.\n` + + ' Available templates: ' + + templates.map((t) => t.id).join(', '), + ); + process.exit(1); + } + + p.log.success(`Template: ${template.name}`); + + await fs.ensureDir(targetDir); + + const downloadSpinner = p.spinner(); + downloadSpinner.start('Downloading template from GitHub...'); + + try { + await fetchTemplate(template, targetDir); + downloadSpinner.stop('Template downloaded'); + } catch (error) { + downloadSpinner.stop('Download failed'); + await fs.remove(targetDir); + p.cancel(`Failed to download template: ${error.message}`); + process.exit(1); + } + + const configSpinner = p.spinner(); + configSpinner.start('Generating configuration files...'); + + try { + const templatePkgPath = path.join(targetDir, 'package.json'); + const examplePkg = await fs.readJson(templatePkgPath); + + const filesToRemove = [ + 'package.json', + 'README.md', + ...template.excludeFiles, + ]; + for (const file of filesToRemove) { + const filePath = path.join(targetDir, file); + if (await fs.pathExists(filePath)) { + await fs.remove(filePath); + } + } + + const rewritePrivateDeps = (deps) => { + if (!deps) return deps; + const rewritten = { ...deps }; + if (rewritten['@stylexjs/shared-ui']) { + rewritten['@stylexjs/shared-ui'] = 'file:./shared-ui'; + } + return rewritten; + }; + + const normalizeScripts = (scripts) => { + if (!scripts) return scripts; + const normalized = {}; + for (const [key, value] of Object.entries(scripts)) { + const normalizedKey = key.replace(/^example:/, ''); + normalized[normalizedKey] = value; + } + return normalized; + }; + + const newPkg = { + name: projectName, + version: '0.1.0', + private: true, + type: examplePkg.type, + scripts: normalizeScripts(examplePkg.scripts), + dependencies: rewritePrivateDeps(examplePkg.dependencies), + devDependencies: rewritePrivateDeps(examplePkg.devDependencies), + }; + + await fs.writeJson(path.join(targetDir, 'package.json'), newPkg, { + spaces: 2, + }); + + const scripts = newPkg.scripts || {}; + const runCommand = scripts.dev + ? 'npm run dev' + : scripts.build + ? 'npm run build' + : scripts.start + ? 'npm run start' + : 'npm run'; + + const readme = `# ${projectName} + +A new StyleX project created with create-stylex-app. + +## Getting Started + +\`\`\`bash +npm install +${runCommand} +\`\`\` + +## Template + +This project uses the **${template.name}** template. +`; + + await fs.writeFile(path.join(targetDir, 'README.md'), readme); + configSpinner.stop('Configuration files generated'); + } catch (error) { + configSpinner.stop('Configuration generation failed'); + await fs.remove(targetDir); + p.cancel(`Failed to generate configuration: ${error.message}`); + process.exit(1); + } + + await finishSetup(argv, projectName, targetDir); +} + +/** + * Handle custom template installation + */ +async function handleCustomTemplate(argv, projectName, targetDir) { + p.log.info(`Using custom template: ${pc.cyan(argv.template)}`); + + await fs.ensureDir(targetDir); + + const downloadSpinner = p.spinner(); + downloadSpinner.start('Downloading custom template...'); + + try { + await fetchCustomTemplate(argv.template, targetDir); + downloadSpinner.stop('Template downloaded'); + } catch (error) { + downloadSpinner.stop('Download failed'); + await fs.remove(targetDir); + p.cancel(`Failed to download custom template: ${error.message}`); + process.exit(1); + } + + const excludePatterns = [ + 'node_modules', + '.next', + 'dist', + '.vite', + 'package-lock.json', + 'yarn.lock', + 'pnpm-lock.yaml', + ]; + for (const file of excludePatterns) { + const filePath = path.join(targetDir, file); + if (await fs.pathExists(filePath)) { + await fs.remove(filePath); + } + } + + const pkgPath = path.join(targetDir, 'package.json'); + if (await fs.pathExists(pkgPath)) { + const pkg = await fs.readJson(pkgPath); + pkg.name = projectName; + + await fs.writeJson(pkgPath, pkg, { spaces: 2 }); + p.log.success('Updated package.json with project name'); + } else { + p.log.warn( + 'No package.json found in template. You may need to create one manually.', + ); + } + + await finishSetup(argv, projectName, targetDir); +} + +/** + * Common setup completion (install deps, show success message) + */ +async function finishSetup(argv, projectName, targetDir) { + const pm = await detectPackageManager(); + + let runScript = 'dev'; + const pkgPath = path.join(targetDir, 'package.json'); + if (await fs.pathExists(pkgPath)) { + const pkg = await fs.readJson(pkgPath); + const scripts = pkg.scripts || {}; + if (scripts.dev) { + runScript = 'dev'; + } else if (scripts.build) { + runScript = 'build'; + } else if (scripts.start) { + runScript = 'start'; + } + } + + if (argv.install) { + const installSpinner = p.spinner({ indicator: 'timer' }); + installSpinner.start(`Installing dependencies with ${pm}...`); + + try { + const result = await installDependencies(targetDir, pm); + const countMsg = result.packageCount + ? ` (${result.packageCount} packages)` + : ''; + installSpinner.stop(`Dependencies installed${countMsg}`); + } catch (error) { + installSpinner.stop('Installation failed'); + const errorMessage = + error instanceof Error ? error.message : String(error); + p.log.error(errorMessage); + // $FlowFixMe[prop-missing] - stderr is added by installDependencies + if (error != null && typeof error === 'object' && error.stderr) { + // $FlowFixMe[incompatible-use] + p.log.error(error.stderr.trim()); + } + p.log.warn( + `You can install dependencies manually: cd ${projectName} && ${pm} install`, + ); + } + } else { + p.log.info('Skipped dependency installation (--no-install)'); + } + + const nextSteps = [ + `cd ${projectName}`, + ...(argv.install ? [] : [`${pm} install`]), + `${pm} run ${runScript}`, + ].join('\n'); + + p.note(nextSteps, 'Next steps'); + + p.outro(`${pc.green('Done!')} Happy coding with StyleX`); +} + +main().catch((error) => { + const errorMessage = error instanceof Error ? error.message : String(error); + p.cancel(errorMessage); + process.exit(1); +}); diff --git a/packages/@stylexjs/create-stylex-app/src/templates.js b/packages/@stylexjs/create-stylex-app/src/templates.js new file mode 100644 index 000000000..6e32cdf37 --- /dev/null +++ b/packages/@stylexjs/create-stylex-app/src/templates.js @@ -0,0 +1,44 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +'use strict'; + +const { fetchTemplatesManifest } = require('./utils/fetch-template'); + +/** + * Bundled templates as fallback if GitHub fetch fails + */ +const BUNDLED_TEMPLATES = require('../templates.json').templates; + +/** + * Get templates from GitHub, falling back to bundled templates + * @returns {Promise} Array of template definitions + */ +async function getTemplates() { + try { + const manifest = await fetchTemplatesManifest(); + if (manifest && Array.isArray(manifest.templates)) { + return manifest.templates; + } + } catch (error) {} + return BUNDLED_TEMPLATES; +} + +/** + * Get bundled templates synchronously (for help text, etc.) + * @returns {Array} Array of template definitions + */ +function getBundledTemplates() { + return BUNDLED_TEMPLATES; +} + +module.exports = { + getTemplates, + getBundledTemplates, + BUNDLED_TEMPLATES, +}; diff --git a/packages/@stylexjs/create-stylex-app/src/utils/fetch-template.js b/packages/@stylexjs/create-stylex-app/src/utils/fetch-template.js new file mode 100644 index 000000000..10a0c18c2 --- /dev/null +++ b/packages/@stylexjs/create-stylex-app/src/utils/fetch-template.js @@ -0,0 +1,110 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +'use strict'; + +const path = require('path'); +const { downloadTemplate } = require('giget'); + +const DEFAULT_REPO = 'facebook/stylex'; +const DEFAULT_BRANCH = 'main'; + +/** + * Fetch shared-ui from the stylex packages directory + */ +async function fetchSharedUI(targetDir) { + const source = `github:${DEFAULT_REPO}/packages/shared-ui#${DEFAULT_BRANCH}`; + const sharedUIDir = path.join(targetDir, 'shared-ui'); + + await downloadTemplate(source, { + dir: sharedUIDir, + force: true, + }); + + return sharedUIDir; +} + +/** + * Fetch a template from the stylex examples directory + */ +async function fetchTemplate(templateConfig, targetDir) { + const source = `github:${DEFAULT_REPO}/examples/${templateConfig.exampleSource}#${DEFAULT_BRANCH}`; + + await downloadTemplate(source, { + dir: targetDir, + force: true, + }); + + if (templateConfig.usesSharedUI) { + await fetchSharedUI(targetDir); + } + + return targetDir; +} + +/** + * Fetch a custom template from a URL or GitHub reference + * Supports: + * - "github:owner/repo/path" + * - "github:owner/repo/path#branch" + * - "gh:owner/repo/path" (shorthand) + * - Full GitHub URLs + */ +async function fetchCustomTemplate(templateSource, targetDir) { + await downloadTemplate(templateSource, { + dir: targetDir, + force: true, + }); + + return targetDir; +} + +/** + * Fetch the templates manifest from GitHub + * This allows template definitions to be updated without updating the CLI + */ +async function fetchTemplatesManifest( + repo = DEFAULT_REPO, + branch = DEFAULT_BRANCH, +) { + const manifestPath = 'packages/@stylexjs/create-stylex-app/templates.json'; + + try { + const fs = require('fs-extra'); + const path = require('path'); + const os = require('os'); + + const tempDir = path.join(os.tmpdir(), `stylex-manifest-${Date.now()}`); + await downloadTemplate( + `github:${repo}/${path.dirname(manifestPath)}#${branch}`, + { + dir: tempDir, + force: true, + }, + ); + + const manifestFile = path.join(tempDir, 'templates.json'); + if (await fs.pathExists(manifestFile)) { + const content = await fs.readJson(manifestFile); + await fs.remove(tempDir); + return content; + } + + await fs.remove(tempDir); + return null; + } catch (error) { + return null; + } +} + +module.exports = { + fetchTemplate, + fetchCustomTemplate, + fetchTemplatesManifest, + fetchSharedUI, +}; diff --git a/packages/@stylexjs/create-stylex-app/src/utils/files.js b/packages/@stylexjs/create-stylex-app/src/utils/files.js new file mode 100644 index 000000000..9572dc52f --- /dev/null +++ b/packages/@stylexjs/create-stylex-app/src/utils/files.js @@ -0,0 +1,37 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +'use strict'; + +const fs = require('fs-extra'); +const path = require('path'); + +/** + * Recursively copy a directory while excluding certain files/directories + */ +async function copyDirectory(source, target, excludePatterns) { + const entries = await fs.readdir(source, { withFileTypes: true }); + + for (const entry of entries) { + if (excludePatterns.includes(entry.name)) { + continue; + } + + const sourcePath = path.join(source, entry.name); + const targetPath = path.join(target, entry.name); + + if (entry.isDirectory()) { + await fs.ensureDir(targetPath); + await copyDirectory(sourcePath, targetPath, excludePatterns); + } else { + await fs.copy(sourcePath, targetPath); + } + } +} + +module.exports = { copyDirectory }; diff --git a/packages/@stylexjs/create-stylex-app/src/utils/packages.js b/packages/@stylexjs/create-stylex-app/src/utils/packages.js new file mode 100644 index 000000000..f4e8ba770 --- /dev/null +++ b/packages/@stylexjs/create-stylex-app/src/utils/packages.js @@ -0,0 +1,87 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * + */ + +const fs = require('fs-extra'); +const spawn = require('cross-spawn'); + +/** + * Detect which package manager to use based on lock files + * @returns {Promise<'npm'|'yarn'|'pnpm'>} + */ +async function detectPackageManager() { + // Check lock files in current directory + if (await fs.pathExists('package-lock.json')) return 'npm'; + if (await fs.pathExists('yarn.lock')) return 'yarn'; + if (await fs.pathExists('pnpm-lock.yaml')) return 'pnpm'; + + // Default to npm + return 'npm'; +} + +/** + * Install dependencies in the target directory + * @param {string} targetDir - Directory where dependencies should be installed + * @param {'npm'|'yarn'|'pnpm'} packageManager - Package manager to use + * @returns {Promise<{packageCount: number|null}>} - Installation result with package count + */ +async function installDependencies(targetDir, packageManager) { + return new Promise((resolve, reject) => { + const child = spawn(packageManager, ['install'], { + cwd: targetDir, + stdio: 'pipe', + }); + + let stdout = ''; + let stderr = ''; + + child.stdout.on('data', (data) => { + stdout += data.toString(); + }); + + child.stderr.on('data', (data) => { + stderr += data.toString(); + }); + + child.on('close', (code) => { + if (code !== 0) { + const error = new Error( + `${packageManager} install failed with exit code ${code}`, + ); + // $FlowFixMe[prop-missing] - Adding custom properties for error context + error.stderr = stderr; + // $FlowFixMe[prop-missing] + error.stdout = stdout; + reject(error); + return; + } + + // Try to extract package count from npm/yarn/pnpm output + let packageCount = null; + const npmMatch = stdout.match(/added (\d+) packages?/i); + // TODO: Add package count extraction for yarn/pnpm + const _yarnMatch = stdout.match(/Done in [\d.]+s/i); + const _pnpmMatch = stdout.match(/packages? are ready/i); + + if (npmMatch) { + packageCount = parseInt(npmMatch[1], 10); + } + + resolve({ packageCount }); + }); + + child.on('error', (error) => { + reject(error); + }); + }); +} + +module.exports = { + detectPackageManager, + installDependencies, +}; diff --git a/packages/@stylexjs/create-stylex-app/templates.json b/packages/@stylexjs/create-stylex-app/templates.json new file mode 100644 index 000000000..5ec32b3aa --- /dev/null +++ b/packages/@stylexjs/create-stylex-app/templates.json @@ -0,0 +1,198 @@ +{ + "version": 1, + "templates": [ + { + "id": "nextjs", + "name": "Next.js (App Router)", + "description": "Full-stack React framework with server components", + "features": ["SSR", "App Router", "TypeScript"], + "recommended": true, + "exampleSource": "example-nextjs", + "excludeFiles": [ + "node_modules", + ".next", + "package-lock.json", + "yarn.lock", + "pnpm-lock.yaml" + ] + }, + { + "id": "vite-react", + "name": "Vite + React", + "description": "Fast development with instant HMR", + "features": ["React", "TypeScript", "Fast HMR"], + "exampleSource": "example-vite-react", + "usesSharedUI": true, + "excludeFiles": [ + "node_modules", + "dist", + ".vite", + "package-lock.json", + "yarn.lock", + "pnpm-lock.yaml" + ] + }, + { + "id": "vite", + "name": "Vite (Vanilla)", + "description": "Lightweight setup without a framework", + "features": ["Vanilla TS", "Fast HMR", "Minimal"], + "exampleSource": "example-vite", + "usesSharedUI": true, + "excludeFiles": [ + "node_modules", + "dist", + ".vite", + "package-lock.json", + "yarn.lock", + "pnpm-lock.yaml" + ] + }, + { + "id": "webpack", + "name": "Webpack + React", + "description": "StyleX with Webpack via @stylexjs/unplugin", + "features": ["Webpack", "React", "HMR"], + "exampleSource": "example-webpack", + "excludeFiles": [ + "node_modules", + "dist", + "package-lock.json", + "yarn.lock", + "pnpm-lock.yaml" + ] + }, + { + "id": "rollup", + "name": "Rollup + React", + "description": "StyleX with Rollup bundler", + "features": ["Rollup", "React"], + "exampleSource": "example-rollup", + "excludeFiles": [ + "node_modules", + "dist", + "package-lock.json", + "yarn.lock", + "pnpm-lock.yaml" + ] + }, + { + "id": "esbuild", + "name": "esbuild + React", + "description": "StyleX with esbuild via @stylexjs/unplugin", + "features": ["esbuild", "React", "Fast builds"], + "exampleSource": "example-esbuild", + "excludeFiles": [ + "node_modules", + "dist", + "package-lock.json", + "yarn.lock", + "pnpm-lock.yaml" + ] + }, + { + "id": "rspack", + "name": "Rspack + React", + "description": "StyleX with Rspack via @stylexjs/unplugin", + "features": ["Rspack", "React", "Fast builds"], + "exampleSource": "example-rspack", + "excludeFiles": [ + "node_modules", + "dist", + "package-lock.json", + "yarn.lock", + "pnpm-lock.yaml" + ] + }, + { + "id": "react-router", + "name": "React Router", + "description": "React Router with RSC support", + "features": ["React Router", "RSC", "TypeScript"], + "exampleSource": "example-react-router", + "usesSharedUI": true, + "excludeFiles": [ + "node_modules", + "dist", + "package-lock.json", + "yarn.lock", + "pnpm-lock.yaml" + ] + }, + { + "id": "waku", + "name": "Waku", + "description": "Minimal React framework with RSC", + "features": ["Waku", "RSC", "TypeScript"], + "exampleSource": "example-waku", + "usesSharedUI": true, + "excludeFiles": [ + "node_modules", + "dist", + "package-lock.json", + "yarn.lock", + "pnpm-lock.yaml" + ] + }, + { + "id": "vite-rsc", + "name": "Vite + RSC", + "description": "Vite with React Server Components", + "features": ["Vite", "RSC", "TypeScript"], + "exampleSource": "example-vite-rsc", + "usesSharedUI": true, + "excludeFiles": [ + "node_modules", + "dist", + ".vite", + "package-lock.json", + "yarn.lock", + "pnpm-lock.yaml" + ] + }, + { + "id": "redwoodsdk", + "name": "RedwoodSDK", + "description": "RedwoodSDK with Cloudflare Workers", + "features": ["RedwoodSDK", "Cloudflare", "RSC"], + "exampleSource": "example-redwoodsdk", + "usesSharedUI": true, + "excludeFiles": [ + "node_modules", + "dist", + "package-lock.json", + "yarn.lock", + "pnpm-lock.yaml" + ] + }, + { + "id": "storybook", + "name": "Storybook", + "description": "Component development with Storybook", + "features": ["Storybook", "Vite", "Testing"], + "exampleSource": "example-storybook", + "excludeFiles": [ + "node_modules", + "dist", + "storybook-static", + "package-lock.json", + "yarn.lock", + "pnpm-lock.yaml" + ] + }, + { + "id": "cli", + "name": "CLI (Standalone)", + "description": "Use StyleX CLI for standalone compilation", + "features": ["CLI", "TypeScript"], + "exampleSource": "example-cli", + "excludeFiles": [ + "node_modules", + "dist", + "package-lock.json", + "yarn.lock", + "pnpm-lock.yaml" + ] + } + ] +} diff --git a/yarn.lock b/yarn.lock index 3ef1a270b..0fb9e0d43 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1655,6 +1655,23 @@ jsonfile "^6.1.0" strip-ansi "^7.1.0" +"@clack/core@0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@clack/core/-/core-0.5.0.tgz#970df024a927d6af90111667a0384e233b5ffa1a" + integrity sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow== + dependencies: + picocolors "^1.0.0" + sisteransi "^1.0.5" + +"@clack/prompts@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@clack/prompts/-/prompts-0.11.0.tgz#5c0218f2b46626a50d72d8a485681eb8d94bd2a7" + integrity sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw== + dependencies: + "@clack/core" "0.5.0" + picocolors "^1.0.0" + sisteransi "^1.0.5" + "@cloudflare/kv-asset-handler@0.4.1": version "0.4.1" resolved "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.1.tgz" @@ -7427,6 +7444,17 @@ autoprefixer@^10.4.12, autoprefixer@^10.4.21, autoprefixer@^10.4.7: picocolors "^1.1.1" postcss-value-parser "^4.2.0" +autoprefixer@^10.4.20: + version "10.4.23" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.23.tgz#c6aa6db8e7376fcd900f9fd79d143ceebad8c4e6" + integrity sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA== + dependencies: + browserslist "^4.28.1" + caniuse-lite "^1.0.30001760" + fraction.js "^5.3.4" + picocolors "^1.1.1" + postcss-value-parser "^4.2.0" + available-typed-arrays@^1.0.7: version "1.0.7" resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz" @@ -7665,7 +7693,7 @@ base64-js@^1.3.1: resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -baseline-browser-mapping@^2.8.25, baseline-browser-mapping@^2.9.14: +baseline-browser-mapping@^2.8.25, baseline-browser-mapping@^2.9.0, baseline-browser-mapping@^2.9.14: version "2.9.14" resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.14.tgz#3b6af0bc032445bca04de58caa9a87cfe921cbb3" integrity sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg== @@ -7851,6 +7879,17 @@ browserslist@^4.0.0, browserslist@^4.18.1, browserslist@^4.21.4, browserslist@^4 node-releases "^2.0.27" update-browserslist-db "^1.1.4" +browserslist@^4.28.1: + version "4.28.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95" + integrity sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA== + dependencies: + baseline-browser-mapping "^2.9.0" + caniuse-lite "^1.0.30001759" + electron-to-chromium "^1.5.263" + node-releases "^2.0.27" + update-browserslist-db "^1.2.0" + bser@2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz" @@ -8033,6 +8072,11 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001579, caniuse-lite@^1.0.30001754: resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001754.tgz" integrity sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg== +caniuse-lite@^1.0.30001759, caniuse-lite@^1.0.30001760: + version "1.0.30001764" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001764.tgz#03206c56469f236103b90f9ae10bcb8b9e1f6005" + integrity sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g== + ccount@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz" @@ -8232,6 +8276,13 @@ ci-info@^4.2.0: resolved "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz" integrity sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA== +citty@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/citty/-/citty-0.1.6.tgz#0f7904da1ed4625e1a9ea7e0fa780981aab7c5e4" + integrity sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ== + dependencies: + consola "^3.2.3" + cjs-module-lexer@^2.1.0: version "2.1.1" resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.1.1.tgz" @@ -8579,6 +8630,11 @@ consola@^2.15.3: resolved "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz" integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw== +consola@^3.2.3, consola@^3.4.0, consola@^3.4.2: + version "3.4.2" + resolved "https://registry.yarnpkg.com/consola/-/consola-3.4.2.tgz#5af110145397bb67afdab77013fdc34cae590ea7" + integrity sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA== + content-disposition@0.5.2: version "0.5.2" resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz" @@ -9491,6 +9547,11 @@ electron-to-chromium@^1.5.249: resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.250.tgz" integrity sha512-/5UMj9IiGDMOFBnN4i7/Ry5onJrAGSbOGo3s9FEKmwobGq6xw832ccET0CE3CkkMBZ8GJSlUIesZofpyurqDXw== +electron-to-chromium@^1.5.263: + version "1.5.267" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz#5d84f2df8cdb6bfe7e873706bb21bd4bfb574dc7" + integrity sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw== + emittery@^0.13.1: version "0.13.1" resolved "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz" @@ -11334,6 +11395,18 @@ get-uri@^6.0.1: data-uri-to-buffer "^6.0.2" debug "^4.3.4" +giget@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/giget/-/giget-2.0.0.tgz#395fc934a43f9a7a29a29d55b99f23e30c14f195" + integrity sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA== + dependencies: + citty "^0.1.6" + consola "^3.4.0" + defu "^6.1.4" + node-fetch-native "^1.6.6" + nypm "^0.6.0" + pathe "^2.0.3" + github-slugger@^1.4.0: version "1.5.0" resolved "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz" @@ -14895,6 +14968,11 @@ node-environment-flags@^1.0.5: object.getownpropertydescriptors "^2.0.3" semver "^5.7.0" +node-fetch-native@^1.6.6: + version "1.6.7" + resolved "https://registry.yarnpkg.com/node-fetch-native/-/node-fetch-native-1.6.7.tgz#9d09ca63066cc48423211ed4caf5d70075d76a71" + integrity sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q== + node-fetch@^2.6.7, node-fetch@^2.7.0: version "2.7.0" resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz" @@ -15013,6 +15091,17 @@ nwsapi@^2.2.16: resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.22.tgz" integrity sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ== +nypm@^0.6.0: + version "0.6.2" + resolved "https://registry.yarnpkg.com/nypm/-/nypm-0.6.2.tgz#467512024948398fafa73cea30a3ed9efc5af071" + integrity sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g== + dependencies: + citty "^0.1.6" + consola "^3.4.2" + pathe "^2.0.3" + pkg-types "^2.3.0" + tinyexec "^1.0.1" + object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" @@ -15561,7 +15650,7 @@ periscopic@^4.0.2: is-reference "^3.0.2" zimmerframe "^1.0.0" -picocolors@1.1.1, picocolors@^1.0.0, picocolors@^1.1.1, picocolors@~1.1.1: +picocolors@1.1.1, picocolors@^1.0.0, picocolors@^1.1.0, picocolors@^1.1.1, picocolors@~1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== @@ -18749,7 +18838,7 @@ tinybench@^2.9.0: resolved "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz" integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg== -tinyexec@^1.0.2: +tinyexec@^1.0.1, tinyexec@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz" integrity sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg== @@ -19448,6 +19537,14 @@ update-browserslist-db@^1.1.4: escalade "^3.2.0" picocolors "^1.1.1" +update-browserslist-db@^1.2.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz#64d76db58713136acbeb4c49114366cc6cc2e80d" + integrity sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + update-check@1.5.4: version "1.5.4" resolved "https://registry.npmjs.org/update-check/-/update-check-1.5.4.tgz"