diff --git a/packages/create-fractal/README.md b/packages/create-fractal/README.md new file mode 100644 index 000000000..4516ee424 --- /dev/null +++ b/packages/create-fractal/README.md @@ -0,0 +1,18 @@ +# @frctl/create-fractal + +New Fractal pattern library initializer. + +[![NPM Version](https://img.shields.io/npm/v/@frctl/create-fractal)](https://www.npmjs.com/package/@frctl/create-fractal) + +## Usage + +Run `npm init @frctl/fractal [dir]` in your terminal. `[dir]` is the new folder you want to create your project in. + +## Testing locally + +1. Find out the full path to this directory. +2. Run `npx [path/to/this/dir]`. + +## License + +[MIT](https://github.com/frctl/fractal/blob/main/LICENSE) diff --git a/packages/create-fractal/package.json b/packages/create-fractal/package.json new file mode 100644 index 000000000..998b6cdbc --- /dev/null +++ b/packages/create-fractal/package.json @@ -0,0 +1,33 @@ +{ + "name": "@frctl/create-fractal", + "version": "0.0.0", + "description": "New Fractal pattern library initializer.", + "bin": "./src/index.js", + "main": "./src/index.js", + "repository": { + "type": "git", + "url": "https://github.com/frctl/fractal.git", + "directory": "packages/create-fractal" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/frctl/fractal/issues" + }, + "homepage": "https://github.com/frctl/fractal", + "dependencies": { + "@frctl/core": "^0.3.3", + "execa": "^5.0.0", + "fs-extra": "^9.1.0", + "handlebars": "^4.7.7", + "inquirer": "^7.3.3" + }, + "devDependencies": { + "@frctl/fractal": "^1.5.11" + }, + "engines": { + "node": ">= 10.0.0" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/create-fractal/src/generate-project.js b/packages/create-fractal/src/generate-project.js new file mode 100644 index 000000000..db606b100 --- /dev/null +++ b/packages/create-fractal/src/generate-project.js @@ -0,0 +1,61 @@ +const path = require('path'); + +const { utils, shell } = require('@frctl/core'); +const fs = require('fs-extra'); +const Handlebars = require('handlebars'); + +const pkg = require('../package.json'); + +module.exports = async ({ targetPath, templatePath, answers }) => { + const componentsDir = path.join(targetPath, answers.componentsDir); + const docsDir = path.join(targetPath, answers.docsDir); + const publicDir = path.join(targetPath, answers.publicDir); + const packageJSONPath = path.join(targetPath, 'package.json'); + const gitIgnorePath = path.join(targetPath, '.gitignore'); + const fractalFilePath = path.join(targetPath, 'fractal.config.js'); + const docsIndexPath = path.join(docsDir, '01-index.md'); + const componentCopyTo = path.join(componentsDir, 'example'); + + const fractalFileTpl = path.join(templatePath, 'fractal.hbs'); + const docsIndexTpl = path.join(templatePath, 'docs/index.md'); + const exampleComponent = path.join(templatePath, 'components/example'); + + const packageJSON = { + name: utils.slugify(answers.projectTitle), + version: '0.1.0', + dependencies: { + '@frctl/fractal': `${pkg.devDependencies['@frctl/fractal']}`, + }, + scripts: { + 'fractal:start': 'fractal start --sync', + 'fractal:build': 'fractal build', + }, + }; + + const fractalContents = Handlebars.compile(fs.readFileSync(fractalFileTpl, 'utf8'))(answers); + const indexContents = Handlebars.compile(fs.readFileSync(docsIndexTpl, 'utf8'))(answers); + + // ensure target folders exist + await fs.ensureDir(targetPath); + await fs.ensureDir(componentsDir); + await fs.ensureDir(docsDir); + await fs.ensureDir(publicDir); + + // write package.json + await fs.writeJson(packageJSONPath, packageJSON, { spaces: 2 }); + + // copy example component + await fs.copy(exampleComponent, componentCopyTo); + + // write git-specific files + if (answers.useGit) { + shell.touch(path.join(publicDir, '.gitkeep')); + await fs.writeFile(gitIgnorePath, 'node_modules\n'); + } + + // write config file + await fs.writeFile(fractalFilePath, fractalContents); + + // write docs index + await fs.writeFile(docsIndexPath, indexContents); +}; diff --git a/packages/create-fractal/src/get-answers.js b/packages/create-fractal/src/get-answers.js new file mode 100644 index 000000000..41ac3061f --- /dev/null +++ b/packages/create-fractal/src/get-answers.js @@ -0,0 +1,40 @@ +const { utils } = require('@frctl/core'); +const inquirer = require('inquirer'); + +// In the future, use template parameter to ask special questions depending on template name. +module.exports = async ({ targetDir }) => { + const questions = [ + { + type: 'input', + name: 'projectTitle', + message: "What's the title of your project?", + default: utils.titlize(targetDir), + }, + { + type: 'input', + name: 'componentsDir', + message: 'Where would you like to keep your components?', + default: 'components', + }, + { + type: 'input', + name: 'docsDir', + message: 'Where would you like to keep your docs?', + default: 'docs', + }, + { + type: 'input', + name: 'publicDir', + message: 'What would you like to call your public directory?', + default: 'public', + }, + { + type: 'confirm', + name: 'useGit', + message: 'Will you use Git for version control on this project?', + default: true, + }, + ]; + + return await inquirer.prompt(questions); +}; diff --git a/packages/create-fractal/src/index.js b/packages/create-fractal/src/index.js new file mode 100755 index 000000000..9170bc41b --- /dev/null +++ b/packages/create-fractal/src/index.js @@ -0,0 +1,69 @@ +#!/usr/bin/env node + +const path = require('path'); + +const { shell } = require('@frctl/core'); +const execa = require('execa'); +const fs = require('fs-extra'); +// const inquirer = require('inquirer'); + +const getAnswers = require('./get-answers'); +const generateProject = require('./generate-project'); + +const templatesDir = path.join(__dirname, '../templates'); + +(async () => { + const targetDir = process.argv.length >= 3 ? process.argv[2] : '.'; + const targetPath = path.join(process.cwd(), targetDir); + + try { + // ensure creating project in a new directory + const pathExists = await fs.pathExists(targetPath); + + if (pathExists) { + console.error(`Cannot create new project: The directory ${targetPath} already exists.`); + return; + } + + // enable when we have multiple starter templates + /* + const { template } = await inquirer.prompt([ + { + type: 'list', + name: 'template', + message: 'What template do you want to start from?', + choices: [ + 'handlebars', + ], + } + ]); + */ + const template = 'handlebars'; + const templatePath = path.join(templatesDir, template); + + console.log('Creating new project.... just a few questions:'); + // ask questions, get answers + const answers = await getAnswers({ + template, + targetDir, + }); + + console.log('Generating project structure...'); + await generateProject({ + targetPath, + templatePath, + answers, + }); + + // do initial install + console.log('Installing dependencies... This may take a while!'); + shell.cd(targetPath); + await execa('npm', ['install'], { stdio: 'inherit' }); + + // success! + console.log('Your new Fractal project has been set up.'); + } catch (e) { + await fs.remove(targetPath); + console.error(e); + } +})(); diff --git a/packages/create-fractal/templates/handlebars/components/example/example.config.yml b/packages/create-fractal/templates/handlebars/components/example/example.config.yml new file mode 100644 index 000000000..aeff704ae --- /dev/null +++ b/packages/create-fractal/templates/handlebars/components/example/example.config.yml @@ -0,0 +1,3 @@ +title: Example component +context: + text: This is an example component! diff --git a/packages/create-fractal/templates/handlebars/components/example/example.hbs b/packages/create-fractal/templates/handlebars/components/example/example.hbs new file mode 100644 index 000000000..1eeeb0597 --- /dev/null +++ b/packages/create-fractal/templates/handlebars/components/example/example.hbs @@ -0,0 +1 @@ +

{{ text }}

diff --git a/packages/create-fractal/templates/handlebars/docs/index.md b/packages/create-fractal/templates/handlebars/docs/index.md new file mode 100644 index 000000000..735fe008a --- /dev/null +++ b/packages/create-fractal/templates/handlebars/docs/index.md @@ -0,0 +1,5 @@ +--- +title: {{ projectTitle }} +--- + +This is your index page. You can edit its contents at `{{ docsDir }}/01-index.md` diff --git a/packages/create-fractal/templates/handlebars/fractal.hbs b/packages/create-fractal/templates/handlebars/fractal.hbs new file mode 100644 index 000000000..177313a9a --- /dev/null +++ b/packages/create-fractal/templates/handlebars/fractal.hbs @@ -0,0 +1,39 @@ +'use strict'; + +/* +* Require the path module +*/ +const path = require('path'); + +/* + * Require the Fractal module + */ +const fractal = module.exports = require('@frctl/fractal').create(); + +{{#if projectTitle}} +/* + * Give your project a title. + */ +fractal.set('project.title', '{{ projectTitle }}'); +{{/if}} + +{{#if componentsDir}} +/* + * Tell Fractal where to look for components. + */ +fractal.components.set('path', path.join(__dirname, '{{ componentsDir }}')); +{{/if}} + +{{#if docsDir}} +/* + * Tell Fractal where to look for documentation pages. + */ +fractal.docs.set('path', path.join(__dirname, '{{ docsDir }}')); +{{/if}} + +{{#if publicDir}} +/* + * Tell the Fractal web preview plugin where to look for static assets. + */ +fractal.web.set('static.path', path.join(__dirname, '{{ publicDir }}')); +{{/if}}