From 6b7cf6f788243b5e4781bf349cb4c464c3614500 Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 14 Mar 2020 14:49:20 -0700 Subject: [PATCH 01/21] init --- .gitignore | 6 +++ .vscode/extensions.json | 5 ++ .vscode/launch.json | 14 +++++ .vscode/settings.json | 6 +++ README.md | 1 + test/utils.js | 115 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 147 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/extensions.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 README.md create mode 100644 test/utils.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e207458 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# NPM +node_modules +package-lock.json + +# Tutorial +CODEROAD.md diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..c0a2258 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": ["dbaeumer.vscode-eslint"] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..cf82cc7 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Run Tests", + "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", + "args": ["--reporter=spec", "--colors", "--bail", "--timeout", "9999999"], + "console": "internalConsole", + "internalConsoleOptions": "openOnSessionStart" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..d4c0ca5 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll": true + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..e13b648 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Backend Challenges: package.json diff --git a/test/utils.js b/test/utils.js new file mode 100644 index 0000000..f5ee461 --- /dev/null +++ b/test/utils.js @@ -0,0 +1,115 @@ +const fs = require("fs"); +const util = require("util"); +const { join } = require("path"); + +const readFile = util.promisify(fs.readFile); +const readdir = util.promisify(fs.readdir); + +const getPackageJson = async (dir = process.cwd()) => { + // load package.json file + const pathToPackageJson = join(dir, "package.json"); + const packageJson = await readFile(pathToPackageJson, "utf8").catch( + console.error + ); + if (!packageJson) { + throw new Error("Missing root package.json"); + } + // parse as JSON + const json = JSON.parse(packageJson); + if (!json) { + throw new Error("The package.json content looks invalid"); + } + return json; +}; + +exports.getPackageJson = getPackageJson; + +const versionMatch = (current, expected) => { + let currentSemver = current; + if (["~", "^"].includes(current[0])) { + currentSemver = current.substring(1); + } + return currentSemver === expected; +}; + +/** + * isModuleInstalled + * @param { name, type } params + * "name" - the name of the dependency + * "type" - "dependency", "devDependency", "peerDependency" + * @returns boolean + */ +const isModuleInstalled = async ({ name, type, version }) => { + // 1. load package.json file + const json = await getPackageJson(); + + // 2. verify package lists dependency by type + let installCommand; + let hasDependency; + let currentVersion; + + switch (type) { + case "dependency": + installCommand = "--save"; + hasDependency = !!json.dependencies && json.dependencies[name]; + currentVersion = json.dependencies[name]; + break; + case "devDependency": + installCommand = "--save-dev"; + hasDependency = !!json.devDependencies && json.devDependencies[name]; + currentVersion = json.devDependencies[name]; + break; + case "peerDependency": + throw new Error("Peer dependencies unsupported"); + default: + throw new Error("Unsupported packaged type"); + } + + if (!hasDependency) { + throw new Error(`Run "npm install ${installCommand} ${name}"`); + } + + // 3. if version, check dependency version + if (version && !versionMatch(currentVersion, version)) { + throw new Error( + `Dependency ${name} version ${currentVersion} does not match expected ${version}` + ); + } + + // 4. verify node_module installed + const pathToNodeModule = join(process.cwd(), "node_modules", name); + const hasNodeModules = await readdir(pathToNodeModule).catch(() => { + throw new Error('Missing node_modules. Run "npm install"'); + }); + if (!hasNodeModules) { + throw new Error('Missing node_modules. Run "npm install"'); + } + + // 5. if version, has installed node_module version + if (version) { + const nodeModulePackageJson = await getPackageJson(pathToNodeModule); + if (!versionMatch(nodeModulePackageJson.version, version)) { + throw new Error( + `Dependency ${name} version ${version} is not yet installed. Run "npm install"` + ); + } + } + + return true; +}; + +exports.isModuleInstalled = isModuleInstalled; + +// created because assert.doesNotThrow not working predictably with async fns +const doesNotThrow = async (fn) => { + let result = true; + try { + await fn(); + } catch (error) { + console.error(error); + result = false; + } + return result; +}; + +exports.doesNotThrow = doesNotThrow; From 8df92a5f48f3b630d9cd59faf83c04237f2cb9a3 Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 14 Mar 2020 14:52:25 -0700 Subject: [PATCH 02/21] 1.1 - author key --- package.json | 20 ++++++++++++++++++++ server.js | 9 +++++++++ test/packagejson.test.js | 18 ++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 package.json create mode 100644 server.js create mode 100644 test/packagejson.test.js diff --git a/package.json b/package.json new file mode 100644 index 0000000..182f189 --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "fcc-learn-npm-package-json", + "repository": { + "type": "git", + "url": "https://github.com/shmck/coderoad-learn-npm-package-json" + }, + "main": "server.js", + "scripts": { + "start": "node server.js", + "programmatic-test": "mocha --reporter=mocha-tap-reporter", + "test": "mocha" + }, + "dependencies": { + "express": "^4.17.0" + }, + "devDependencies": { + "mocha": "^7.0.1", + "mocha-tap-reporter": "^0.1.3" + } +} diff --git a/server.js b/server.js new file mode 100644 index 0000000..2fb2ef4 --- /dev/null +++ b/server.js @@ -0,0 +1,9 @@ +const express = require("express"); +const app = express(); +const port = 3000; + +app.get("/", (req, res) => res.send("Hello World!")); + +app.listen(port, () => + console.log(`Example app listening at http://localhost:${port} !`) +); diff --git a/test/packagejson.test.js b/test/packagejson.test.js new file mode 100644 index 0000000..190d6bb --- /dev/null +++ b/test/packagejson.test.js @@ -0,0 +1,18 @@ +const { getPackageJson } = require("./utils"); +const assert = require("assert"); + +describe("package.json", () => { + let json; + before(async () => { + json = await getPackageJson(); + }); + // 1.1 + it('should have a valid "author" key', () => { + assert.ok(json.author, 'no "author" key provided'); + assert.equal( + typeof json.author, + "string", + 'should have an "author" value that is a string' + ); + }); +}); From 49577c720003a1f8e797f7047d7b81211dfb54b0 Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 14 Mar 2020 14:53:10 -0700 Subject: [PATCH 03/21] 1.1 author solution --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 182f189..4914d7d 100644 --- a/package.json +++ b/package.json @@ -16,5 +16,6 @@ "devDependencies": { "mocha": "^7.0.1", "mocha-tap-reporter": "^0.1.3" - } + }, + "author": "Your Name " } From c9718b2d87d2b86ac64ac4f99c68841b019d78a1 Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 14 Mar 2020 15:01:09 -0700 Subject: [PATCH 04/21] 1.2 description --- test/packagejson.test.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/packagejson.test.js b/test/packagejson.test.js index 190d6bb..c8e0917 100644 --- a/test/packagejson.test.js +++ b/test/packagejson.test.js @@ -15,4 +15,13 @@ describe("package.json", () => { 'should have an "author" value that is a string' ); }); + // 1.2 + it('should have a valid "description" key', () => { + assert.ok(json.description, '"description" is missing'); + assert.equal( + typeof json.description, + "string", + 'should have a "description" value that is a string' + ); + }); }); From a15aa990f06d7cb8e206e476d525e8bde84e5ecf Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 14 Mar 2020 15:01:15 -0700 Subject: [PATCH 05/21] 1.2 description solution --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 4914d7d..8d807da 100644 --- a/package.json +++ b/package.json @@ -17,5 +17,6 @@ "mocha": "^7.0.1", "mocha-tap-reporter": "^0.1.3" }, - "author": "Your Name " + "author": "Your Name ", + "description": "A description of the project" } From 2b907aec30676e097b1ffc4c18e06c6e71e5cb4f Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 14 Mar 2020 15:08:00 -0700 Subject: [PATCH 06/21] 1.3 keywords --- test/packagejson.test.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/packagejson.test.js b/test/packagejson.test.js index c8e0917..91b7bfe 100644 --- a/test/packagejson.test.js +++ b/test/packagejson.test.js @@ -24,4 +24,16 @@ describe("package.json", () => { 'should have a "description" value that is a string' ); }); + // 1.3 + it('should have a valid "keywords" key', () => { + assert.ok(json.keywords, '"keywords" is missing'); + assert.ok( + Array.isArray(json.keywords), + 'should have a "keywords" value that is an array' + ); + assert.ok( + json.keywords.includes("freecodecamp"), + 'should include "freecodecamp"' + ); + }); }); From 6342f6418c6bee990d094cff6385a520de853d10 Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 14 Mar 2020 15:08:07 -0700 Subject: [PATCH 07/21] 1.3 keywords solution --- package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 8d807da..78d1e77 100644 --- a/package.json +++ b/package.json @@ -18,5 +18,8 @@ "mocha-tap-reporter": "^0.1.3" }, "author": "Your Name ", - "description": "A description of the project" + "description": "A description of the project", + "keywords": [ + "freecodecamp" + ] } From 96e391557e7d389c461243f671040c05bb1d837e Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 14 Mar 2020 15:12:48 -0700 Subject: [PATCH 08/21] 1.4 license --- test/packagejson.test.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/packagejson.test.js b/test/packagejson.test.js index 91b7bfe..51a7222 100644 --- a/test/packagejson.test.js +++ b/test/packagejson.test.js @@ -36,4 +36,13 @@ describe("package.json", () => { 'should include "freecodecamp"' ); }); + // 1.4 + it('should have a valid "license" key', () => { + assert.ok(json.license, '"license" is missing'); + assert.equal( + typeof json.license, + "string", + 'should have a "license" value that is a string, e.g. "MIT"' + ); + }); }); From ec61d78c442d05b22c5f2dfa3fd7f515ac02e03b Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 14 Mar 2020 15:12:53 -0700 Subject: [PATCH 09/21] 1.4 license solution --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 78d1e77..f33a196 100644 --- a/package.json +++ b/package.json @@ -21,5 +21,6 @@ "description": "A description of the project", "keywords": [ "freecodecamp" - ] + ], + "license": "MIT" } From 8ed877a7b9404bf68f0a441aa176be4cbdcd904d Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 14 Mar 2020 15:27:54 -0700 Subject: [PATCH 10/21] 1.5 version --- test/packagejson.test.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/packagejson.test.js b/test/packagejson.test.js index 51a7222..d43bd5e 100644 --- a/test/packagejson.test.js +++ b/test/packagejson.test.js @@ -45,4 +45,13 @@ describe("package.json", () => { 'should have a "license" value that is a string, e.g. "MIT"' ); }); + // 1.5 + it('should have a valid "version" key', () => { + assert.ok(json.version, '"version" is missing'); + assert.equal( + typeof json.version, + "string", + 'should have a "version" value that is a string' + ); + }); }); From 5be16a25c8d16d457d7c569c503445e1e62014a2 Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 14 Mar 2020 15:28:03 -0700 Subject: [PATCH 11/21] 1.5 version solution --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index f33a196..6b4963b 100644 --- a/package.json +++ b/package.json @@ -22,5 +22,6 @@ "keywords": [ "freecodecamp" ], - "license": "MIT" + "license": "MIT", + "version": "0.1.0" } From f89980ab544f50176f955f03d079c450db237f7e Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 14 Mar 2020 18:41:24 -0700 Subject: [PATCH 12/21] 2.1 install moment --- test/packagejson.test.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/test/packagejson.test.js b/test/packagejson.test.js index d43bd5e..9530b09 100644 --- a/test/packagejson.test.js +++ b/test/packagejson.test.js @@ -1,4 +1,4 @@ -const { getPackageJson } = require("./utils"); +const { getPackageJson, isModuleInstalled, doesNotThrow } = require("./utils"); const assert = require("assert"); describe("package.json", () => { @@ -54,4 +54,21 @@ describe("package.json", () => { 'should have a "version" value that is a string' ); }); + // 2.1 + it('should have "dependencies"', () => { + assert.ok(json.dependencies, '"dependencies" is missing'); + assert.equal( + typeof json.dependencies, + "object", + 'should have a "dependencies" value that is an object' + ); + }); + it('should have installed "moment"', async () => { + assert.ok( + await doesNotThrow( + () => isModuleInstalled({ name: "moment", type: "dependency" }), + '"moment" not installed' + ) + ); + }); }); From 6bcac253044db2bfed502df6bbb37bb294aae5e0 Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 14 Mar 2020 18:41:30 -0700 Subject: [PATCH 13/21] 2.1 install moment solution --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 6b4963b..c6fb687 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "test": "mocha" }, "dependencies": { - "express": "^4.17.0" + "express": "^4.17.0", + "moment": "^2.24.0" }, "devDependencies": { "mocha": "^7.0.1", From c7b54588f08c566d17e50df1639f0ee6c2238fcf Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 14 Mar 2020 19:11:14 -0700 Subject: [PATCH 14/21] 2.2 semver --- test/packagejson.test.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/packagejson.test.js b/test/packagejson.test.js index 9530b09..498e7b2 100644 --- a/test/packagejson.test.js +++ b/test/packagejson.test.js @@ -71,4 +71,25 @@ describe("package.json", () => { ) ); }); + // 2.2 + it('should use specific version of "moment"', () => { + assert.equal( + json.dependencies.moment, + "2.10.2", + '"moment" should use the specific version' + ); + }); + it('should have installed specific version of "moment"', async () => { + assert.ok( + await doesNotThrow( + () => + isModuleInstalled({ + name: "moment", + type: "dependency", + version: "2.10.2" + }), + '"moment" not installed' + ) + ); + }); }); From 7854c2f71fbbf85ee7ee13405d98454adc64cb7f Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 14 Mar 2020 19:11:21 -0700 Subject: [PATCH 15/21] 2.2 semver solution --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c6fb687..8d3b594 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "express": "^4.17.0", - "moment": "^2.24.0" + "moment": "2.10.2" }, "devDependencies": { "mocha": "^7.0.1", From ff88ec91d274c999177b48dae0e6b0a2ebb361d7 Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 14 Mar 2020 20:02:08 -0700 Subject: [PATCH 16/21] 2.3 patch release --- test/packagejson.test.js | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/test/packagejson.test.js b/test/packagejson.test.js index 498e7b2..cc880de 100644 --- a/test/packagejson.test.js +++ b/test/packagejson.test.js @@ -71,25 +71,11 @@ describe("package.json", () => { ) ); }); - // 2.2 - it('should use specific version of "moment"', () => { - assert.equal( - json.dependencies.moment, - "2.10.2", - '"moment" should use the specific version' - ); - }); - it('should have installed specific version of "moment"', async () => { + // 2.3 + it('should allow npm to update to any patch release of "moment"', () => { assert.ok( - await doesNotThrow( - () => - isModuleInstalled({ - name: "moment", - type: "dependency", - version: "2.10.2" - }), - '"moment" not installed' - ) + json.dependencies.moment.match(/^~/), + '"moment" should specify a patch release with "~"' ); }); }); From a4b27008c3691b692e10c9fe95f7284f4af425d4 Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 14 Mar 2020 20:02:13 -0700 Subject: [PATCH 17/21] 2.3 patch release solution --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8d3b594..bd051f0 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "express": "^4.17.0", - "moment": "2.10.2" + "moment": "~2.10.2" }, "devDependencies": { "mocha": "^7.0.1", From dd5615784e7453cd0aad1f83cbb35357df1a1cd2 Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 14 Mar 2020 20:03:15 -0700 Subject: [PATCH 18/21] 2.4 minor release --- test/packagejson.test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/packagejson.test.js b/test/packagejson.test.js index cc880de..8b1700f 100644 --- a/test/packagejson.test.js +++ b/test/packagejson.test.js @@ -71,11 +71,11 @@ describe("package.json", () => { ) ); }); - // 2.3 - it('should allow npm to update to any patch release of "moment"', () => { + // 2.4 + it('should allow npm to update to any minor release of "moment"', () => { assert.ok( - json.dependencies.moment.match(/^~/), - '"moment" should specify a patch release with "~"' + json.dependencies.moment.match(/^\^/), + '"moment" should specify a minor release with "^"' ); }); }); From a9ec3c0a837d465992cfca09098c396d625d70ee Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 14 Mar 2020 20:03:22 -0700 Subject: [PATCH 19/21] 2.4 minor release solution --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bd051f0..4b9d414 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "express": "^4.17.0", - "moment": "~2.10.2" + "moment": "^2.10.2" }, "devDependencies": { "mocha": "^7.0.1", From 9e35b51316ee56472944819dfc89c0f1bdd65cfe Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 14 Mar 2020 20:21:24 -0700 Subject: [PATCH 20/21] 2.5 remove moment --- test/packagejson.test.js | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/test/packagejson.test.js b/test/packagejson.test.js index 8b1700f..7833e88 100644 --- a/test/packagejson.test.js +++ b/test/packagejson.test.js @@ -63,19 +63,11 @@ describe("package.json", () => { 'should have a "dependencies" value that is an object' ); }); - it('should have installed "moment"', async () => { + // 2.5 + it('should remove the dependency "moment"', () => { assert.ok( - await doesNotThrow( - () => isModuleInstalled({ name: "moment", type: "dependency" }), - '"moment" not installed' - ) - ); - }); - // 2.4 - it('should allow npm to update to any minor release of "moment"', () => { - assert.ok( - json.dependencies.moment.match(/^\^/), - '"moment" should specify a minor release with "^"' + !json.dependencies.moment, + '"moment" should be removed as a dependency' ); }); }); From 9536de2b79a90adb087e592ac538a248fa54f3a5 Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 14 Mar 2020 20:21:32 -0700 Subject: [PATCH 21/21] 2.5 remove moment solution --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 4b9d414..6b4963b 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,7 @@ "test": "mocha" }, "dependencies": { - "express": "^4.17.0", - "moment": "^2.10.2" + "express": "^4.17.0" }, "devDependencies": { "mocha": "^7.0.1", pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy