Skip to content

Commit 00ffccf

Browse files
committed
this is now fun
1 parent 74fad40 commit 00ffccf

File tree

6 files changed

+93
-24
lines changed

6 files changed

+93
-24
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Cover:
2626
- interfaces
2727
- classes and the tricky bit about private fields
2828
- vscode: type hints, useful error messages
29-
29+
- function types and that stupid thing where you have to supply a name
3030

3131
## Section 2: The runtime. Node and the many dialects of JavaScript
3232

SecretDungeonCrawl.js

Lines changed: 37 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package-lock.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@
2323
"dependencies": {
2424
"boxen": "^1.3.0",
2525
"chalk": "^2.4.1",
26-
"inquirer": "^6.0.0"
26+
"inquirer": "^6.0.0",
27+
"lodash": "^4.17.10"
2728
},
2829
"devDependencies": {
2930
"@types/inquirer": "0.0.42",
31+
"@types/lodash": "^4.14.111",
3032
"@types/node": "^10.5.2",
3133
"ts-node": "^7.0.0",
3234
"typescript": "^2.9.2"

src/cli/support/SecretDungeonCrawl.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,22 @@
33
* This must not import anything unglobal
44
*/
55

6-
export interface ModuleResolutionTrap {
7-
error: Error;
8-
paths: string[];
9-
filename: string;
6+
export class ModuleResolutionError extends Error {
7+
public readonly kind = "ModuleResolutionError"
8+
constructor(public cause: Error, public paths: string[], public filename: string) {
9+
super(cause.message);
10+
}
11+
}
12+
13+
export function isModuleResolutionError(e: Error): e is ModuleResolutionError {
14+
const maybe = e as ModuleResolutionError;
15+
return maybe.kind === "ModuleResolutionError";
1016
}
1117

1218
export interface NodeModuleResolutionExposed {
1319
resolvePaths(resolveMe: string): string[] | null;
1420

15-
locateModule(lib: string): string | ModuleResolutionTrap;
21+
locateModule(lib: string): string;
1622

1723
filename: string;
1824
}
@@ -27,11 +33,7 @@ export const Crawl: NodeModuleResolutionExposed = {
2733
try {
2834
return require.resolve(lib);
2935
} catch (error) {
30-
return {
31-
error,
32-
paths: require.resolve.paths(lib) || [],
33-
filename: __filename,
34-
}
36+
throw new ModuleResolutionError(error, require.resolve.paths(lib) || [], __filename);
3537
}
3638
},
3739

src/cli/support/youAreIn.ts

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
1-
21
import * as fs from "fs";
32
import * as path from "path";
43
import chalk from "chalk";
54
import * as inquirer from "inquirer";
5+
import * as _ from "lodash";
66

7-
import { PackageJSON } from "package-json";
7+
import { PackageJSON, DependencyMap } from "package-json";
88
import { outputCurrentState, output, outputDoom, debug } from "./output";
99
import { promisify } from "util";
10-
import { Crawl as LocalCrawl, NodeModuleResolutionExposed } from "./SecretDungeonCrawl";
10+
import { Crawl as LocalCrawl, NodeModuleResolutionExposed, isModuleResolutionError } from "./SecretDungeonCrawl";
11+
12+
// want to:
13+
// - make it find roots of packages, because it fails when it goes into a lib
14+
// - make it guess why it couldn't resolve a dev dependency
15+
// - make it "check GPS" to tell you what dir it's in
16+
// - make it report how many stairs you went up or down
17+
// - make it report the difference in versions?
1118

1219
const readFile = promisify(fs.readFile);
1320
const writeFile = promisify(fs.writeFile);
@@ -20,9 +27,9 @@ export async function youAreIn(appDir: string) {
2027
debug("Hello from " + appDir);
2128

2229
if (circumstances === "not a package") {
23-
output(`You are in ${appDir}. It is completely dark in here. What even is this place?? `);
30+
outputDoom(`You are in ${appDir}.\nIt is completely dark in here.\nWhat even is this place?? `);
2431
} else if (circumstances === "invalid package json") {
25-
output(`A rat bites your foot! The package.json is invalid in ${appDir}`);
32+
outputDoom(`A rat bites your foot! The package.json is invalid in ${appDir}`);
2633
} else {
2734
outputCurrentState(`You are in "${circumstances.packageJson.name}". It appears to be version ${circumstances.packageJson.version}.`)
2835
return timeToAct({ ...circumstances, crawl: await injectSecretDungeonCrawl(appDir) });
@@ -45,23 +52,24 @@ async function injectSecretDungeonCrawl(appDir: string): Promise<NodeModuleResol
4552

4653
async function timeToAct(p: PackageRoot & { crawl: NodeModuleResolutionExposed }): Promise<void> {
4754
const answers = await requestNextAction(p);
48-
output(`You have chosen: ${answers.action}`)
55+
debug(`You have chosen: ${answers.action}`)
4956
switch (answers.action) {
5057
case "exit":
5158
return;
5259
case "doors":
5360
output(`You have examined all the doors before you, and chosen: ${answers.door}`);
5461
const otherSide = findLibraryRoot(answers.door, p.crawl);
5562
if (itsaTrap(otherSide)) {
56-
outputDoom(chalk.red("It's a trap! ") + otherSide.error.message)
63+
outputDoom(chalk.red("It's a trap! ") + otherSide.details || otherSide.error.message)
5764
return youAreIn(p.appDir);
5865
}
5966
return youAreIn(otherSide);
6067
}
6168
}
6269

6370
type Trap = {
64-
error: Error
71+
error: Error,
72+
details?: string
6573
}
6674

6775
function itsaTrap(t: Trap | string): t is Trap {
@@ -74,7 +82,9 @@ function findLibraryRoot(lib: string, crawl: NodeModuleResolutionExposed): strin
7482
try {
7583
whereIsIt = crawl.locateModule(lib);
7684
} catch (error) {
77-
return { error };
85+
const details = isModuleResolutionError(error) ?
86+
`${error.message}\nfrom ${error.filename}\nPaths searched: ${error.paths.join("\n")}` : undefined;
87+
return { error, details };
7888
}
7989
debug(`Resolved ${lib} to ${whereIsIt}`);
8090
const dir = path.dirname(whereIsIt);
@@ -109,10 +119,22 @@ async function requestNextAction(p: PackageRoot): Promise<NextActionAnswers> {
109119
return response;
110120
}
111121

122+
function choicesFromDependencyObject(optionalDeps: DependencyMap | undefined,
123+
colorFn: (txt: string) => string): inquirer.objects.ChoiceOption[] {
124+
const deps = optionalDeps || {};
125+
return Object.keys(deps).map(d => ({
126+
value: d,
127+
name: colorFn(d + ":" + deps[d])
128+
}));
129+
}
130+
112131
function chooseDoor(p: PackageRoot): inquirer.Question<NextActionAnswers> {
113-
const listOfDependencies: inquirer.ChoiceType[] = Object.keys(p.packageJson.dependencies || {})
114-
.concat(Object.keys(p.packageJson.devDependencies || {}).map(devdep => chalk.gray(devdep)))
115-
.sort()
132+
const allDependencies = choicesFromDependencyObject(p.packageJson.dependencies, chalk.white)
133+
.concat(choicesFromDependencyObject(p.packageJson.devDependencies, chalk.grey))
134+
.concat(choicesFromDependencyObject(p.packageJson.peerDependencies, chalk.magenta));
135+
const listOfDependencies = _.sortBy(
136+
allDependencies,
137+
ct => ct.value as string)
116138
// debug("The dependencies are: " + listOfDependencies.join(" & "))
117139
return {
118140
name: "door",

0 commit comments

Comments
 (0)