Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: lang select #19

Merged
merged 21 commits into from
Jun 12, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
## 0.1.0 (May 25, 2020)
* Initial Release

- Initial Release
55 changes: 0 additions & 55 deletions cli.js

This file was deleted.

2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#!/usr/bin/env node
require("./cli")().catch(console.error);
require("./lib/cli")(process.argv.slice(2)).catch(console.error);
115 changes: 115 additions & 0 deletions lib/cli.js
Original file line number Diff line number Diff line change
@@ -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(
/(<html.*lang=)\"([^"]*)\"/gi,
`$1"${lang}"`
);
await fs.writeFile(indexFile, resultHTML);
} catch (err) {
/* istanbul ignore next */
throw new Error(err);
}
};
742 changes: 742 additions & 0 deletions lib/countries.json

Large diffs are not rendered by default.

14 changes: 8 additions & 6 deletions package-lock.json
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -20,22 +20,24 @@
"scripts": {
"test": "jest",
"format": "prettier --write \"./cli.js\" \"tests/*.js\"",
"coverage": "jest --coverage --collectCoverageOnlyFrom ./cli.js",
"start": "node ./index.js ./out/example"
"coverage": "jest --coverage --collectCoverageOnlyFrom ./lib/cli.js"
},
"files": [
"lib/**/*",
"license.txt",
"index.js",
"cli.js",
"README.md"
],
"dependencies": {
"chalk": "^4.0.0",
"elapsed-time-logger": "^1.1.2",
"fast-glob": "^3.2.2",
"fs-extra": "^9.0.0",
"fuzzy": "^0.1.3",
"ora": "^4.0.4",
"pacote": "^11.1.10",
"prompts": "^2.3.2",
"semver": "^7.3.2",
"yargs-parser": "^18.1.3"
},
"devDependencies": {
77 changes: 55 additions & 22 deletions tests/test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const fs = require("fs-extra");
const cli = require("../cli");
const cli = require("../lib/cli");
const os = require("os");
const packageName = "html5-boilerplate";
const tempDir = os.tmpdir() + `/${packageName}-staging`;
@@ -31,34 +31,46 @@ const cases = [
"--release=7.3.0",
...all_versions.map((v) => "-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}`);
});
});