diff options
Diffstat (limited to 'packages/create-astro')
-rw-r--r-- | packages/create-astro/package.json | 1 | ||||
-rw-r--r-- | packages/create-astro/src/config.ts | 19 | ||||
-rw-r--r-- | packages/create-astro/src/frameworks.ts | 109 | ||||
-rw-r--r-- | packages/create-astro/src/index.ts | 98 | ||||
-rw-r--r-- | packages/create-astro/src/templates.ts | 4 |
5 files changed, 219 insertions, 12 deletions
diff --git a/packages/create-astro/package.json b/packages/create-astro/package.json index 9b50f4039..f9ed461f6 100644 --- a/packages/create-astro/package.json +++ b/packages/create-astro/package.json @@ -23,6 +23,7 @@ "create-astro.js" ], "dependencies": { + "node-fetch": "^2.6.1", "@types/degit": "^2.8.2", "@types/prompts": "^2.0.12", "degit": "^2.8.4", diff --git a/packages/create-astro/src/config.ts b/packages/create-astro/src/config.ts new file mode 100644 index 000000000..6a2b3cb64 --- /dev/null +++ b/packages/create-astro/src/config.ts @@ -0,0 +1,19 @@ +export const createConfig = ({ renderers }: { renderers: string[] }) => { + return [`export default { + // projectRoot: '.', // Where to resolve all URLs relative to. Useful if you have a monorepo project. + // pages: './src/pages', // Path to Astro components, pages, and data + // dist: './dist', // When running \`astro build\`, path to final static output + // public: './public', // A folder of static files Astro will copy to the root. Useful for favicons, images, and other files that don’t need processing. + buildOptions: { + // site: 'http://example.com', // Your public domain, e.g.: https://my-site.dev/. Used to generate sitemaps and canonical URLs. + sitemap: true, // Generate sitemap (set to "false" to disable) + }, + devOptions: { + // port: 3000, // The port to run the dev server on. + // tailwindConfig: '', // Path to tailwind.config.js if used, e.g. './tailwind.config.js' + },`, + ` renderers: ${JSON.stringify(renderers, undefined, 2).split('\n').map((ln, i) => i !== 0 ? ` ${ln}` : ln).join('\n')},`, +`}; +`].join('\n') +} + diff --git a/packages/create-astro/src/frameworks.ts b/packages/create-astro/src/frameworks.ts new file mode 100644 index 000000000..f7db2e0f6 --- /dev/null +++ b/packages/create-astro/src/frameworks.ts @@ -0,0 +1,109 @@ +export const COUNTER_COMPONENTS = { + '@astrojs/renderer-preact': { + filename: `src/components/PreactCounter.jsx`, + content: `import { h } from 'preact'; +import { useState } from 'preact/hooks'; + +export default function PreactCounter({ children }) { + const [count, setCount] = useState(0); + const add = () => setCount((i) => i + 1); + const subtract = () => setCount((i) => i - 1); + + return ( + <div id="preact" class="counter"> + <button onClick={subtract}>-</button> + <pre>{count}</pre> + <button onClick={add}>+</button> + </div> + ); +} +` + }, + '@astrojs/renderer-react': { + filename: `src/components/ReactCounter.jsx`, + content: `import React, { useState } from 'react'; + +export default function ReactCounter({ children }) { + const [count, setCount] = useState(0); + const add = () => setCount((i) => i + 1); + const subtract = () => setCount((i) => i - 1); + + return ( + <div id="react" className="counter"> + <button onClick={subtract}>-</button> + <pre>{count}</pre> + <button onClick={add}>+</button> + </div> + ); +} +` + }, + '@astrojs/renderer-svelte': { + filename: `src/components/SvelteCounter.svelte`, + content: `<script> + let count = 0; + + function add() { + count += 1; + } + + function subtract() { + count -= 1; + } +</script> + +<div id="svelte" class="counter"> + <button on:click={subtract}>-</button> + <pre>{ count }</pre> + <button on:click={add}>+</button> +</div> +` + }, + '@astrojs/renderer-vue': { + filename: `src/components/VueCounter.vue`, + content: `<template> + <div id="vue" class="counter"> + <button @click="subtract()">-</button> + <pre>{{ count }}</pre> + <button @click="add()">+</button> + </div> +</template> + +<script> +import { ref } from 'vue'; +export default { + setup() { + const count = ref(0) + const add = () => count.value = count.value + 1; + const subtract = () => count.value = count.value - 1; + + return { + count, + add, + subtract + } + } +} +</script> +` + } +}; + +export const FRAMEWORKS = [ + { + title: 'Preact', + value: '@astrojs/renderer-preact', + }, + { + title: 'React', + value: '@astrojs/renderer-react', + }, + { + title: 'Svelte', + value: '@astrojs/renderer-svelte', + }, + { + title: 'Vue', + value: '@astrojs/renderer-vue', + } +]; diff --git a/packages/create-astro/src/index.ts b/packages/create-astro/src/index.ts index e3487e8d4..dcb87086e 100644 --- a/packages/create-astro/src/index.ts +++ b/packages/create-astro/src/index.ts @@ -1,10 +1,13 @@ import fs from 'fs'; import path from 'path'; import { bold, cyan, gray, green, red } from 'kleur/colors'; +import fetch from 'node-fetch'; import prompts from 'prompts'; import degit from 'degit'; import yargs from 'yargs-parser'; +import { FRAMEWORKS, COUNTER_COMPONENTS } from './frameworks.js'; import { TEMPLATES } from './templates.js'; +import { createConfig } from './config.js'; const args = yargs(process.argv); prompts.override(args); @@ -19,7 +22,7 @@ export function mkdirp(dir: string) { const { version } = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf-8')); -const POSTPROCESS_FILES = ['package.json']; // some files need processing after copying. +const POSTPROCESS_FILES = ['package.json', 'astro.config.mjs', 'CHANGELOG.md']; // some files need processing after copying. export async function main() { console.log('\n' + bold('Welcome to Astro!') + gray(` (create-astro v${version})`)); @@ -40,6 +43,9 @@ export async function main() { if (!response.forceOverwrite) { process.exit(1); } + + await fs.promises.rm(cwd, { recursive: true }); + mkdirp(cwd); } } else { mkdirp(cwd); @@ -54,6 +60,10 @@ export async function main() { }, ]); + if (!options.template) { + process.exit(1); + } + const hash = args.commit ? `#${args.commit}` : ''; const templateTarget = options.template.includes('/') ? @@ -66,6 +76,25 @@ export async function main() { verbose: false, }); + const selectedTemplate = TEMPLATES.find(template => template.value === options.template); + let renderers: string[] = []; + + if (selectedTemplate?.renderers === true) { + const result = /** @type {import('./types/internal').Options} */ await prompts([ + { + type: 'multiselect', + name: 'renderers', + message: 'Which frameworks would you like to use?', + choices: FRAMEWORKS, + }, + ]); + renderers = result.renderers; + } else if (selectedTemplate?.renderers && Array.isArray(selectedTemplate.renderers)) { + renderers = selectedTemplate.renderers; + const titles = renderers.map(renderer => FRAMEWORKS.find(item => item.value === renderer)?.title).join(', '); + console.log(green(`✔`) + bold(` Using template's default renderers`) + gray(' › ') + titles); + } + // Copy try { // emitter.on('info', info => { console.log(info.message) }); @@ -78,20 +107,65 @@ export async function main() { } // Post-process in parallel - await Promise.all( - POSTPROCESS_FILES.map(async (file) => { - const fileLoc = path.join(cwd, file); - - switch (file) { - case 'package.json': { - const packageJSON = JSON.parse(await fs.promises.readFile(fileLoc, 'utf8')); - delete packageJSON.snowpack; // delete snowpack config only needed in monorepo (can mess up projects) - await fs.promises.writeFile(fileLoc, JSON.stringify(packageJSON, undefined, 2)); + await Promise.all(POSTPROCESS_FILES.map(async (file) => { + const fileLoc = path.resolve(path.join(cwd, file)); + + switch (file) { + case 'CHANGELOG.md': { + if (fs.existsSync(fileLoc)) { + await fs.promises.rm(fileLoc); + } + break; + } + case 'astro.config.mjs': { + if (selectedTemplate?.renderers !== true) { break; } + await fs.promises.writeFile(fileLoc, createConfig({ renderers })); + break; } - }) - ); + case 'package.json': { + const packageJSON = JSON.parse(await fs.promises.readFile(fileLoc, 'utf8')); + delete packageJSON.snowpack; // delete snowpack config only needed in monorepo (can mess up projects) + // Fetch latest versions of selected renderers + const rendererEntries = await Promise.all(['astro', ...renderers].map((renderer: string) => fetch(`https://registry.npmjs.org/${renderer}/latest`).then((res: any) => res.json()).then((res: any) => [renderer, `^${res['version']}`]))) as any; + packageJSON.devDependencies = { ...packageJSON.devDependencies ?? {}, ...Object.fromEntries(rendererEntries) } + await fs.promises.writeFile(fileLoc, JSON.stringify(packageJSON, undefined, 2)); + break; + } + } + })); + + // Inject framework components into starter template + if (selectedTemplate?.value === 'starter') { + let importStatements: string[] = []; + let components: string[] = []; + await Promise.all(renderers.map(async renderer => { + const component = COUNTER_COMPONENTS[renderer as keyof typeof COUNTER_COMPONENTS]; + const componentName = path.basename(component.filename, path.extname(component.filename)); + const absFileLoc = path.resolve(cwd, component.filename); + importStatements.push(`import ${componentName} from '${component.filename.replace(/^src/, '..')}';`); + components.push(`<${componentName}:visible />`); + await fs.promises.writeFile(absFileLoc, component.content); + })); + + const pageFileLoc = path.resolve(path.join(cwd, 'src', 'pages', 'index.astro')); + const content = (await fs.promises.readFile(pageFileLoc)).toString(); + const lines = content.split('\n'); + const indent = ' '; + const doc = `\n<!-- + - Use imported Framework Components directly in your markup! + - + - Note: by default, components are NOT interactive on the client. + - The \`:visible\` directive tells Astro to make it interactive. + - + - See https://github.com/snowpackjs/astro/blob/main/docs/core-concepts/component-hydration.md + --> +`; + lines.splice(41, 0, importStatements.length > 0 ? doc.split('\n').map(ln => `${indent}${ln}`).join('\n') : '', ...components.map(ln => `${indent}${ln}`)); + lines.splice(3, 0, importStatements.length > 0 ? `// Framework Component Imports` : '', ...importStatements); + await fs.promises.writeFile(pageFileLoc, lines.join('\n')) + } console.log(bold(green('✔') + ' Done!')); diff --git a/packages/create-astro/src/templates.ts b/packages/create-astro/src/templates.ts index 02daaef32..6852aafb0 100644 --- a/packages/create-astro/src/templates.ts +++ b/packages/create-astro/src/templates.ts @@ -2,17 +2,21 @@ export const TEMPLATES = [ { title: 'Starter Kit (Generic)', value: 'starter', + renderers: true, }, { title: 'Blog', value: 'blog', + renderers: ['@astrojs/renderer-preact'] }, { title: 'Documentation', value: 'docs', + renderers: ['@astrojs/renderer-preact'] }, { title: 'Portfolio', value: 'portfolio', + renderers: ['@astrojs/renderer-preact'] }, ]; |