diff --git a/.eslintignore b/.eslintignore index 8085b7f92..216d536ef 100644 --- a/.eslintignore +++ b/.eslintignore @@ -3,6 +3,8 @@ **/node_modules/** !.eslintrc.cjs !.mocharc.js -packages/init/src/template/src/**/*.js +packages/init/src/template/** +packages/init/test/cases/**/output/** +packages/init/test/cases/**/my-app/** packages/plugin-babel/test/cases/**/*main.js TODO.md \ No newline at end of file diff --git a/.gitignore b/.gitignore index bd89ae68f..5acf30c1f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ coverage/ node_modules/ packages/init/test/**/my-app +packages/init/test/**/output packages/**/test/**/yarn.lock packages/**/test/**/package-lock.json packages/**/test/**/netlify diff --git a/packages/init/README.md b/packages/init/README.md index 4626e9ee3..c6a82f497 100644 --- a/packages/init/README.md +++ b/packages/init/README.md @@ -1,29 +1,41 @@ # @greenwood/init ## Overview + Init package for scaffolding out a new Greenwood project. For more information and complete docs, please visit the [Greenwood website](https://www.greenwoodjs.io/docs). ## Usage -Create a directory and then run the `init` command to scaffold a minimal Greenwood project. +Run the `init` command to scaffold a minimal Greenwood project into a directory of your choosing. ```bash -mkdir my-app && cd my-app -npx @greenwood/init@latest +# providing an output directory of my-app +npx @greenwood/init@latest my-app ``` -This will then output the following +This will then output your project files into a directory called _my-app_ ```bash -├── greenwood.config.js +my-app ├── .gitignore +├── greenwood.config.js ├── package.json └── src/ - └─ pages/ - └─ index.md + └─ ... ``` ## API +### Project Name + +By providing a name as the first argument, the `init` command will output the project files into a directory of the same name and configure the `name` property _package.json_. + +```bash +# example +npx @greenwood/init@latest my-app +``` + +> _Omitting my-app will install project files into the current directory._ + ### Template To scaffold your new project based on one of [Greenwood's starter templates](https://github.com/orgs/ProjectEvergreen/repositories?q=greenwood-template-&type=all&language=&sort=), pass the `--template` flag and then follow the prompts to complete the scaffolding. diff --git a/packages/init/src/index.js b/packages/init/src/index.js index 9cdd3eea6..691c6db2f 100755 --- a/packages/init/src/index.js +++ b/packages/init/src/index.js @@ -28,11 +28,11 @@ const templateStandardName = 'greenwood-template-'; let selectedTemplate = null; const scriptPkg = JSON.parse(fs.readFileSync(fileURLToPath(new URL('../package.json', import.meta.url)), 'utf-8')); let templateDir = fileURLToPath(new URL('./template', import.meta.url)); -const TARGET_DIR = process.cwd(); +let TARGET_DIR = process.cwd(); const clonedTemplateDir = path.join(TARGET_DIR, '.greenwood', '.template'); console.log(`${chalk.rgb(175, 207, 71)('-------------------------------------------------------')}`); -console.log(`${chalk.rgb(175, 207, 71)('Initialize Greenwood Template ♻️')}`); +console.log(`${chalk.rgb(175, 207, 71)('Initialize a Greenwood Project ♻️')}`); console.log(`${chalk.rgb(175, 207, 71)('-------------------------------------------------------')}`); const program = new commander.Command(scriptPkg.name) @@ -53,7 +53,7 @@ const npmInit = async () => { const appPkg = JSON.parse(await fs.promises.readFile(path.join(templateDir, '/package.json'), 'utf-8')); // use installation path's folder name for packages - appPkg.name = path.basename(process.cwd()); + appPkg.name = path.basename(TARGET_DIR); // make sure users get latest and greatest version of Greenwood // https://github.com/ProjectEvergreen/greenwood/issues/781 @@ -68,7 +68,7 @@ const npmInit = async () => { // Copy root and src files to target directory const srcInit = async () => { - const templateFiles = []; + let templateFiles = []; await createGitIgnore(); @@ -76,6 +76,12 @@ const srcInit = async () => { templateFiles.push(file); }); + if (program.yarn) { + // we only need .npmrc if we're using npm + // because npm struggles with peer dependencies :/ + templateFiles = templateFiles.filter(file => file !== '.npmrc'); + } + await Promise.all( templateFiles.map(async file => { const resolvedPath = path.join(templateDir, file); @@ -217,7 +223,7 @@ const listAndSelectTemplate = async () => { }); if (selectedTemplate) { - console.log('\Installing Selected Template:', selectedTemplate.name); + console.log('Installing Selected Template:', selectedTemplate.name); } }); } @@ -247,6 +253,20 @@ const cleanUp = async () => { const run = async () => { try { + const firstArg = process.argv[process.argv.length - 1].split(' ')[0]; + const taskRunner = program.yarn ? 'yarn' : 'npm run'; + const shouldChangeDirectory = !firstArg.startsWith('--') && firstArg !== ''; + + if (!firstArg.startsWith('--') && firstArg !== '') { + TARGET_DIR = path.join(TARGET_DIR, `./${firstArg}`); + + if (!fs.existsSync(TARGET_DIR)) { + fs.mkdirSync(TARGET_DIR); + } + } + + console.log(`Initializing into project directory... ${TARGET_DIR}`); + if (program.template) { await listAndSelectTemplate(); @@ -260,7 +280,7 @@ const run = async () => { console.log('Initializing project with files...'); await srcInit(); - console.log('Creating manifest (package.json)...'); + console.log('Creating package.json...'); await npmInit(); if (program.install || program.yarn) { @@ -271,6 +291,12 @@ const run = async () => { await cleanUp(); console.log(`${chalk.rgb(175, 207, 71)('Initializing new project complete!')}`); + + if (shouldChangeDirectory) { + console.log(`Change directories by running => cd ${firstArg}`); + } + + console.log(`To start developing run => ${taskRunner} dev`); } catch (err) { console.error(err); } diff --git a/packages/init/src/template/.npmrc b/packages/init/src/template/.npmrc new file mode 100644 index 000000000..e9ee3cb4d --- /dev/null +++ b/packages/init/src/template/.npmrc @@ -0,0 +1 @@ +legacy-peer-deps=true \ No newline at end of file diff --git a/packages/init/src/template/package.json b/packages/init/src/template/package.json index c52c8c51f..4bc36f1c5 100644 --- a/packages/init/src/template/package.json +++ b/packages/init/src/template/package.json @@ -4,6 +4,7 @@ "description": "", "type": "module", "scripts": { + "dev": "greenwood develop", "start": "greenwood develop", "build": "greenwood build", "serve": "greenwood serve" diff --git a/packages/init/src/template/src/pages/index.html b/packages/init/src/template/src/pages/index.html index 908ffcce6..a519f44b2 100644 --- a/packages/init/src/template/src/pages/index.html +++ b/packages/init/src/template/src/pages/index.html @@ -2,11 +2,6 @@ Greenwood - - - - - diff --git a/packages/init/test/cases/build.default/build.default.spec.js b/packages/init/test/cases/build.default/build.default.spec.js index 16a45e617..fe53e530d 100644 --- a/packages/init/test/cases/build.default/build.default.spec.js +++ b/packages/init/test/cases/build.default/build.default.spec.js @@ -3,10 +3,10 @@ * Scaffold from minimal template and run Greenwood build command. * * User Result - * Should scaffold from template and run the buildå. + * Should scaffold from template and run the build. * * User Command - * @greenwood/init --install && greenwood buildå + * @greenwood/init --install && greenwood build * * User Workspace * N / A diff --git a/packages/init/test/cases/develop.default/develop.default.spec.js b/packages/init/test/cases/develop.default/develop.default.spec.js index febd785c0..0f8f44620 100644 --- a/packages/init/test/cases/develop.default/develop.default.spec.js +++ b/packages/init/test/cases/develop.default/develop.default.spec.js @@ -108,18 +108,10 @@ xdescribe('Scaffold Greenwood and Run Develop command: ', function() { done(); }); - it('should display My Project heading', function(done) { - const heading = dom.window.document.querySelector('body > h2'); - - expect(heading.textContent).to.equal('My Project'); - - done(); - }); - - it('should display My Project title', function(done) { + it('should display default project title', function(done) { const title = dom.window.document.querySelector('head > title'); - expect(title.textContent).to.equal('My App'); + expect(title.textContent).to.equal('Greenwood'); done(); }); diff --git a/packages/init/test/cases/init.default/init.default.spec.js b/packages/init/test/cases/init.default/init.default.spec.js index d83b9a6b4..73e2c046a 100644 --- a/packages/init/test/cases/init.default/init.default.spec.js +++ b/packages/init/test/cases/init.default/init.default.spec.js @@ -58,6 +58,10 @@ describe('Scaffold Greenwood With Default Template: ', function() { expect(fs.existsSync(path.join(outputPath, 'package-lock.json'))).to.be.false; }); + it('should generate a .npmrc file', function() { + expect(fs.existsSync(path.join(outputPath, '.npmrc'))).to.be.true; + }); + it('should not generate a yarn.lock file', function() { expect(fs.existsSync(path.join(outputPath, 'yarn.lock'))).to.be.false; }); @@ -77,7 +81,8 @@ describe('Scaffold Greenwood With Default Template: ', function() { it('the should have the correct Greenwood scripts', function() { const scripts = pkgJson.scripts; - expect(scripts.start).to.equal('greenwood develop'); + expect(scripts.dev).to.equal('greenwood develop'); + expect(scripts.start).to.equal(scripts.dev); expect(scripts.build).to.equal('greenwood build'); expect(scripts.serve).to.equal('greenwood serve'); }); diff --git a/packages/init/test/cases/init.project-name/init.project-name.spec.js b/packages/init/test/cases/init.project-name/init.project-name.spec.js new file mode 100644 index 000000000..38eedbc2e --- /dev/null +++ b/packages/init/test/cases/init.project-name/init.project-name.spec.js @@ -0,0 +1,69 @@ +/* + * Use Case + * Scaffold into a custom project directory + * + * User Result + * Should scaffold out the new project into a my-app directory. + * + * User Command + * npx @greenwood/init my-app + * + * User Workspace + * N / A + */ +import chai from 'chai'; +import fs from 'fs'; +import path from 'path'; +import { Runner } from 'gallinago'; +import { fileURLToPath, URL } from 'url'; + +const expect = chai.expect; + +describe('Scaffold Greenwood into a custom directory: ', function() { + const initPath = path.join(process.cwd(), 'packages/init/src/index.js'); + const outputPath = fileURLToPath(new URL('./output', import.meta.url)); + const projectName = 'my-app'; + let runner; + + before(function() { + this.context = { + publicDir: path.join(outputPath, 'public') + }; + runner = new Runner(); + }); + + describe(`Default output to a custom ${projectName} directory`, function () { + + before(function() { + runner.setup(outputPath); + runner.runCommand(initPath, `${projectName} --foo=bar`); + }); + + describe('expected scaffolding output', function () { + + it('should create a src/pages directory', function() { + expect(fs.existsSync(path.join(outputPath, projectName, 'src', 'pages'))).to.be.true; + }); + + it('should generate a .gitignore file', function() { + expect(fs.existsSync(path.join(outputPath, projectName, '.gitignore'))).to.be.true; + }); + + it('should generate a package.json file', function() { + expect(fs.existsSync(path.join(outputPath, projectName, 'package.json'))).to.be.true; + }); + + it('should have the name in package.json match the project name argument', function() { + const packageJson = JSON.parse(fs.readFileSync(path.join(outputPath, projectName, 'package.json'), 'utf-8')); + + expect(packageJson.name).to.equal(projectName); + }); + }); + + }); + + after(function() { + runner.teardown([outputPath]); + }); + +}); \ No newline at end of file diff --git a/packages/init/test/cases/init.yarn/init.yarn.spec.js b/packages/init/test/cases/init.yarn/init.yarn.spec.js index 4728e9667..0782622a7 100644 --- a/packages/init/test/cases/init.yarn/init.yarn.spec.js +++ b/packages/init/test/cases/init.yarn/init.yarn.spec.js @@ -58,6 +58,10 @@ xdescribe('Scaffold Greenwood With Yarn: ', function() { expect(fs.existsSync(path.join(outputPath, 'package-lock.json'))).to.be.false; }); + it('should not generate a .npmrc file', function() { + expect(fs.existsSync(path.join(outputPath, '.npmrc'))).to.be.false; + }); + it('should generate a public directory', function() { expect(fs.existsSync(path.join(outputPath, 'public'))).to.be.true; }); diff --git a/www/pages/docs/index.md b/www/pages/docs/index.md index c3af8caf5..9ae7e0dad 100644 --- a/www/pages/docs/index.md +++ b/www/pages/docs/index.md @@ -10,7 +10,7 @@ This is the documentation space for **Greenwood** that we hope will help you get ### Installation -Greenwood can be installed with any of the common package managers available today. +Greenwood can be installed manually with any of the common package managers available today or you scaffold a new project through our [`init` package](/getting-started/quick-start/#init-package). ```bash # npm diff --git a/www/pages/getting-started/quick-start.md b/www/pages/getting-started/quick-start.md index aaf3962c8..3b05ed66a 100644 --- a/www/pages/getting-started/quick-start.md +++ b/www/pages/getting-started/quick-start.md @@ -17,12 +17,10 @@ If you want to get right into the code, we have a few options to get you started ### Init Package -You can use Greenwood's [`init` package](https://github.com/ProjectEvergreen/greenwood/blob/master/packages/init/README.md) to scaffold out a new empty Greenwood project, or from a layout. +You can use Greenwood's [`init` package](https://github.com/ProjectEvergreen/greenwood/blob/master/packages/init/README.md) to scaffold out a new empty Greenwood project or use one our template starter kits. ```bash -mkdir my-app && cd my-app - -npx @greenwood/init@latest +$ npx @greenwood/init@latest my-app ``` ### Command Line