diff --git a/CHANGELOG.md b/CHANGELOG.md index 5550f46..8be1fd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +1,3 @@ ## 0.1.0 (May 25, 2020) -* Initial Release + +- Initial Release diff --git a/cli.js b/cli.js deleted file mode 100644 index 44c5e41..0000000 --- a/cli.js +++ /dev/null @@ -1,55 +0,0 @@ -"use strict"; -const yargs_parser = require("yargs-parser"); -const path = require("path"); -const chalk = require("chalk"); -const ora = require("ora"); -const { extract } = require("pacote"); -const glob = require("fast-glob"); -const fs = require("fs-extra"); -const os = require("os"); -const packageName = "html5-boilerplate"; -const tempDir = os.tmpdir() + `/${packageName}-staging`; -const elapsed = require("elapsed-time-logger"); - -module.exports = async () => { - const argv = yargs_parser(process.argv.slice(2), { - alias: { release: ["r"] }, - }); - const timer = elapsed.start(); - const version = argv["release"] || "latest"; - const targetDir = path.resolve(argv["_"][0] || "./"); - const spinner = ora( - `Downloading ${packageName} version '${version}' to ${targetDir}` - ).start(); - await fs.ensureDir(tempDir); - try { - const { from: nameWithVersion } = await extract( - packageName + "@" + version, - tempDir, - {} - ); - spinner.text = `${nameWithVersion} copied to ${targetDir} in ${timer.get()}. Have fun!`; - } catch (err) { - await fs.remove(tempDir); - if (err.code === "ETARGET") { - const msg = chalk.red( - `version '${err.wanted}' not found in npm registry\navailable versions:\n` - ); - spinner.fail(msg + err.versions.reverse().join(" | ")); - throw err.code; - } - spinner.fail("Unexpected error"); - throw new Error(err); - } - await fs.copy(tempDir + "/dist", targetDir); - await fs.remove(tempDir); - // see https://github.com/mrmlnc/fast-glob#how-to-write-patterns-on-windows - const npmIgnoreFiles = await glob( - `${targetDir.replace(/\\/g, "/")}/**/.npmignore` - ); - for (const npmIgnore of npmIgnoreFiles) { - await fs.rename(npmIgnore, npmIgnore.replace(/\.npmignore$/, ".gitignore")); - } - spinner.succeed(); - return; -}; diff --git a/index.js b/index.js index 7c5a75a..f5c9ea7 100644 --- a/index.js +++ b/index.js @@ -1,2 +1,2 @@ #!/usr/bin/env node -require("./cli")().catch(console.error); +require("./lib/cli")(process.argv.slice(2)).catch(console.error); diff --git a/lib/cli.js b/lib/cli.js new file mode 100644 index 0000000..3195121 --- /dev/null +++ b/lib/cli.js @@ -0,0 +1,115 @@ +"use strict"; +const yargs_parser = require("yargs-parser"); +const path = require("path"); +const chalk = require("chalk"); +const langsList = require("./countries.json"); +const prompts = require("prompts"); +// const semver = require("semver"); +const fuzzy = require("fuzzy"); +const ora = require("ora"); +const { extract } = require("pacote"); +const glob = require("fast-glob"); +const fs = require("fs-extra"); +const os = require("os"); +const packageName = "html5-boilerplate"; +const tempDir = os.tmpdir() + `/${packageName}-staging`; +const elapsed = require("elapsed-time-logger"); +let spinner; + +module.exports = async (argvs) => { + const argv = yargs_parser(argvs, { + alias: { release: ["r"], yes: ["y"] }, + }); + const timer = elapsed.start(); + const version = argv["release"] || "latest"; + const targetDir = path.resolve(argv["_"][0] || "./"); + spinner = ora( + `Downloading ${packageName} version '${version}' to ${targetDir}` + ).start(); + await fs.ensureDir(tempDir); + try { + const { from: nameWithVersion } = await extract( + packageName + "@" + version, + tempDir, + {} + ); + await fs.copy(tempDir + "/dist", targetDir); + const timerDownloaded = timer.get(); + await onLoad(targetDir, version, argv); + spinner.succeed( + `${nameWithVersion} copied to ${targetDir} in ${timerDownloaded}. Have fun!` + ); + return; + } catch (err) { + if (err.code === "ETARGET") { + const msg = chalk.red( + `version '${err.wanted}' not found in npm registry\navailable versions:\n` + ); + spinner.fail(msg + err.versions.reverse().join(" | ")); + throw err.code; + } + spinner.fail("Unexpected error"); + throw new Error(err); + } finally { + await fs.remove(tempDir); + } +}; + +const onLoad = async (targetDir, version, argv) => { + // see https://github.com/mrmlnc/fast-glob#how-to-write-patterns-on-windows + const npmIgnoreFiles = await glob( + `${targetDir.replace(/\\/g, "/")}/**/.npmignore` + ); + for (const npmIgnore of npmIgnoreFiles) { + await fs.rename(npmIgnore, npmIgnore.replace(/\.npmignore$/, ".gitignore")); + } + const skipPrompts = argv["yes"] === true; + + spinner.stop(); + if (skipPrompts) { + return; + } + let langListOut = langsList.map((v) => { + return { title: `${v.title} (${v.value})`, value: v.value }; + }); + langListOut.splice(1, 0, { title: "Enter custom", value: "custom" }); + const questions = [ + { + type: "autocomplete", + name: "langChoice", + message: "Select language", + choices: langListOut, + suggest: /* istanbul ignore next */ async (input, choices) => { + return fuzzy + .filter(input, choices, { extract: (el) => el.title }) + .map((v) => v.original); + }, + }, + ]; + let lang = argv.lang; + /* istanbul ignore if */ + if (!lang) { + let { langChoice } = await prompts(questions); + if (langChoice === "custom") { + let { customLang } = await prompts({ + type: "text", + name: "customLang", + message: "Enter custom language code", + }); + langChoice = customLang; + } + lang = langChoice || ""; + } + try { + const indexFile = targetDir + "/index.html"; + const sourceHTML = await fs.readFile(indexFile, "utf-8"); + const resultHTML = sourceHTML.replace( + /( "-r=" + v), ]; -const versionFolder = (version = null) => - version ? `./out/${version}` : defaultDir; -const runCli = async (version = null) => { +const outputFolder = (version = null) => "./out/" + (version || "default_dir"); + +const runCli = async ({ + version = null, + dir = null, + skip = false, + lang = null, +}) => { + let argvs = []; let prevCwd; - if (version) { - process.argv.push(version); - process.argv.push(versionFolder(version)); + if (dir) { + argvs.push("./out/" + dir); } else { await fs.ensureDir(defaultDir); prevCwd = process.cwd(); process.chdir(defaultDir); } - await cli(); + if (version) { - process.argv = process.argv.filter( - (v) => v !== version && v !== versionFolder(version) - ); //revert process args - } else { + argvs.push(version); + } + if (skip) { + argvs.push("-y"); + } + if (lang) { + argvs.push("--lang=" + lang); + } + + await cli(argvs); + if (prevCwd) { process.chdir(prevCwd); //revert process current dir } }; describe.each(cases)("Downloading %s", (version) => { beforeAll(async () => { - await runCli(version); + await runCli({ version: version, dir: version, skip: true }); }); afterAll(async () => { - await fs.remove(versionFolder(version)); + await fs.remove(outputFolder(version)); }); if (version && version != "-r=latest") { @@ -83,17 +95,17 @@ describe.each(cases)("Downloading %s", (version) => { } test("Target directory exists", async () => { - const outDirExists = await fs.exists(versionFolder(version)); + const outDirExists = await fs.exists(outputFolder(version)); expect(outDirExists).toBe(true); }); test("Target directory have files", async () => { - const dirContents = await fs.readdir(versionFolder(version)); + const dirContents = await fs.readdir(outputFolder(version)); expect(dirContents.length).toBeGreaterThanOrEqual(7); }); test("Target directory contains specific files", async () => { - const dirContents = await fs.readdir(versionFolder(version)); + const dirContents = await fs.readdir(outputFolder(version)); const check = [ "index.html", "robots.txt", @@ -108,7 +120,7 @@ describe.each(cases)("Downloading %s", (version) => { test("Target directory contains img/.gitignore", async () => { const imgGitIgnore = await fs.exists( - versionFolder(version) + "/img/.gitignore" + outputFolder(version) + "/img/.gitignore" ); expect(imgGitIgnore).toBe(true); }); @@ -124,11 +136,15 @@ describe("Errors", () => { //maybe create test.each() for more errors scenarios const version = "-r=6..2.3"; try { - await runCli(version); + await runCli({ + version: version, + dir: version, + skip: true, + }); } catch (err) { expect(err).toBe("ETARGET"); } finally { - await fs.remove(versionFolder(version)); + await fs.remove(outputFolder(version)); } }); }); @@ -138,11 +154,28 @@ describe("Unexpected errors", () => { //maybe create test.each() for more errors scenarios const version = "-r=6..2.3,7.2.3"; try { - await runCli(version); + await runCli({ + version: version, + dir: version, + skip: true, + }); } catch (err) { expect(err).not.toBe("ETARGET"); } finally { - await fs.remove(versionFolder(version)); + await fs.remove(outputFolder(version)); } }); }); + +describe("lang", () => { + test("lang", async () => { + const lang = "en-US"; + await runCli({ + lang: "en-US", + dir: `lang_${lang}`, + }); + const fileContent = await fs.readFile("./out/lang_en-US/index.html"); + expect(fileContent.indexOf(`lang="${lang}"`) > -1).toBe(true); + await fs.remove(`./out/lang_${lang}`); + }); +});