diff --git a/packages/create-analog/__tests__/cli.spec.ts b/packages/create-analog/__tests__/cli.spec.ts index fc60853ae..e6bd22618 100644 --- a/packages/create-analog/__tests__/cli.spec.ts +++ b/packages/create-analog/__tests__/cli.spec.ts @@ -1,7 +1,7 @@ -import { join } from 'node:path'; import type { ExecaSyncReturnValue, SyncOptions } from 'execa'; import { commandSync } from 'execa'; import { mkdirpSync, readdirSync, remove, writeFileSync } from 'fs-extra'; +import { join } from 'node:path'; import { afterEach, beforeAll, expect, test } from 'vitest'; const CLI_PATH = join(__dirname, '..'); @@ -33,7 +33,12 @@ templateFiles.push('.git'); templateFiles = templateFiles .map((filePath) => (filePath === '_gitignore' ? '.gitignore' : filePath)) .sort(); - +// starter with tailwind +const templateFilesTailwind = [ + ...templateFiles, + 'tailwind.config.js', + 'postcss.config.js', +].sort(); beforeAll(() => remove(genPath)); afterEach(() => remove(genPath)); @@ -78,9 +83,12 @@ test('asks to overwrite non-empty current directory', () => { }); test('successfully scaffolds a project based on angular starter template', () => { - const { stdout } = run([projectName, '--template', 'angular-v15'], { - cwd: __dirname, - }); + const { stdout } = run( + [projectName, '--template', 'angular-v15', '--skipTailwind'], + { + cwd: __dirname, + } + ); const generatedFiles = readdirSync(genPath).sort(); // Assertions @@ -89,7 +97,7 @@ test('successfully scaffolds a project based on angular starter template', () => }); test('works with the -t alias', () => { - const { stdout } = run([projectName, '-t', 'angular-v15'], { + const { stdout } = run([projectName, '-t', 'angular-v15', '--skipTailwind'], { cwd: __dirname, }); const generatedFiles = readdirSync(genPath).sort(); diff --git a/packages/create-analog/files/postcss.config.js b/packages/create-analog/files/postcss.config.js new file mode 100644 index 000000000..12a703d90 --- /dev/null +++ b/packages/create-analog/files/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/packages/create-analog/files/styles.css b/packages/create-analog/files/styles.css new file mode 100644 index 000000000..6c77ebbd3 --- /dev/null +++ b/packages/create-analog/files/styles.css @@ -0,0 +1,84 @@ +/* Tailwind directives */ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* You can add global styles to this file, and also import other style files */ +:root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + 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; + -webkit-text-size-adjust: 100%; +} + +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; +} + +.card { + padding: 2em; +} + +.logo { + @apply box-content; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/packages/create-analog/files/tailwind.config.js b/packages/create-analog/files/tailwind.config.js new file mode 100644 index 000000000..dbd1c5c33 --- /dev/null +++ b/packages/create-analog/files/tailwind.config.js @@ -0,0 +1,8 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ['./index.html', './src/**/*.{html,ts,md}'], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/packages/create-analog/index.js b/packages/create-analog/index.js index 4e75bf7f3..f32b81baa 100755 --- a/packages/create-analog/index.js +++ b/packages/create-analog/index.js @@ -1,13 +1,13 @@ #!/usr/bin/env node // @ts-check +import { green, red, reset, yellow } from 'kolorist'; +import minimist from 'minimist'; +import { execSync } from 'node:child_process'; import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; -import minimist from 'minimist'; import prompts from 'prompts'; -import { red, reset, yellow, green } from 'kolorist'; -import { execSync } from 'node:child_process'; // Avoids autoconversion to number of the project name by defining that the args // non associated with an option ( _ ) needs to be parsed as a string. See #4606 @@ -44,6 +44,7 @@ const renameFiles = { async function init() { let targetDir = formatTargetDir(argv._[0]); let template = argv.template || argv.t; + let skipTailwind = argv.skipTailwind || false; const defaultTargetDir = 'analog-project'; const getProjectName = () => @@ -123,6 +124,11 @@ async function init() { }; }), }, + { + type: skipTailwind ? null : 'confirm', + name: 'tailwind', + message: 'Would you like to add Tailwind to your project?', + }, ], { onCancel: () => { @@ -136,7 +142,7 @@ async function init() { } // user choice associated with prompts - const { framework, overwrite, packageName, variant } = result; + const { framework, overwrite, packageName, variant, tailwind } = result; const root = path.join(cwd, targetDir); @@ -148,6 +154,7 @@ async function init() { // determine template template = variant || framework || template; + skipTailwind = !tailwind || skipTailwind; console.log(`\nScaffolding project in ${root}...`); @@ -157,6 +164,8 @@ async function init() { `template-${template}` ); + const filesDir = path.resolve(fileURLToPath(import.meta.url), '..', `files`); + const write = (file, content) => { const targetPath = renameFiles[file] ? path.join(root, renameFiles[file]) @@ -173,6 +182,12 @@ async function init() { write(file); } + if (!skipTailwind) { + addTailwindConfig(write, filesDir); + addPostCssConfig(write, filesDir); + addTailwindDirectives(write, filesDir); + } + const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent); const pkgManager = pkgInfo ? pkgInfo.name : 'npm'; const pkg = JSON.parse( @@ -182,6 +197,8 @@ async function init() { pkg.name = packageName || getProjectName(); pkg.scripts.start = getStartCommand(pkgManager); + if (!skipTailwind) addDevDependencies(pkg); + write('package.json', JSON.stringify(pkg, null, 2)); console.log(`\nInitializing git repository:`); @@ -302,6 +319,36 @@ function getStartCommand(pkgManager) { return pkgManager === 'yarn' ? 'yarn dev' : `${pkgManager} run dev`; } +function addTailwindDirectives(write, filesDir) { + write( + 'src/styles.css', + fs.readFileSync(path.join(filesDir, `styles.css`), 'utf-8') + ); +} + +function addPostCssConfig(write, filesDir) { + write( + 'postcss.config.js', + fs.readFileSync(path.join(filesDir, `postcss.config.js`), 'utf-8') + ); +} + +function addTailwindConfig(write, filesDir) { + write( + 'tailwind.config.js', + fs.readFileSync(path.join(filesDir, `tailwind.config.js`), 'utf-8') + ); +} + +function addDevDependencies(pkg) { + ['tailwindcss@^3.3.1', 'postcss@^8.4.21', 'autoprefixer@^10.4.14'].forEach( + (packageName) => { + const [name, version] = packageName.split('@'); + pkg.devDependencies[name] = version; + } + ); +} + init().catch((e) => { console.error(e); }); diff --git a/packages/create-analog/package.json b/packages/create-analog/package.json index 250a7adb9..07df2a2cf 100644 --- a/packages/create-analog/package.json +++ b/packages/create-analog/package.json @@ -1,6 +1,6 @@ { "name": "create-analog", - "version": "0.2.9", + "version": "0.2.10", "type": "module", "license": "MIT", "author": "Brandon Roberts", @@ -10,7 +10,8 @@ }, "files": [ "index.js", - "template-*" + "template-*", + "files" ], "main": "index.js", "engines": {