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

build: add eslint & prettier #14

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
41 changes: 41 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: CI

on:
push:
branches:
- master
tags:
- v[0-9]+.[0-9]+.[0-9]+*
pull_request:

jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest, macOS-latest, ubuntu-latest]
node-version: [10.x, 12.x, 14.x]

steps:
- name: Fix git checkout line endings
run: git config --global core.autocrlf input
- uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/[email protected]
with:
node-version: ${{ matrix.node-version }}
- name: Get yarn cache
id: yarn-cache
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/[email protected]
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/package.json') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install
run: yarn
- name: Lint
run: yarn lint
- name: Test
run: yarn test
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lib
13 changes: 0 additions & 13 deletions .travis.yml

This file was deleted.

7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
Flora Colossus
-----------
## Flora Colossus

> Walk your node_modules tree

Expand Down Expand Up @@ -39,5 +38,5 @@ const walker = new Walker(modulePath);
Returns `Promise<Module[]>`

Will walk your entire node_modules tree reporting back an array of "modules", each
module has a "path", "name" and "depType". See the typescript definition file
for more information.
module has a "path", "name" and "depType". See the typescript definition file
for more information.
56 changes: 56 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"typings": "lib/index.d.ts",
"scripts": {
"build": "tsc",
"lint": "prettier --check . && eslint --ext .ts .",
"prepare": "npm run build",
"pretest": "cd test/fixtures/xml2js && yarn --frozen-lockfile",
"test": "mocha --require ts-node/register test/*_spec.ts"
Expand All @@ -25,13 +26,68 @@
"@types/debug": "^4.1.5",
"@types/fs-extra": "^8.1.0",
"@types/mocha": "^7.0.1",
"@typescript-eslint/eslint-plugin": "^4.9.0",
"@typescript-eslint/parser": "^4.9.0",
"chai": "^4.2.0",
"eslint": "^7.15.0",
"eslint-config-prettier": "^7.0.0",
"eslint-plugin-import": "^2.20.1",
"eslint-plugin-mocha": "^8.0.0",
"eslint-plugin-prettier": "^3.1.2",
"eslint-plugin-promise": "^4.2.1",
"husky": "^4.2.1",
"lint-staged": "^10.0.7",
"mocha": "^7.0.1",
"prettier": "^2.0.2",
"sane": "^4.1.0",
"ts-node": "^8.6.2",
"typescript": "^3.2.2"
},
"engines": {
"node": ">= 10.0.0"
},
"eslintConfig": {
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "2018",
"sourceType": "module"
},
"plugins": [
"@typescript-eslint",
"mocha"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:import/errors",
"plugin:import/warnings",
"plugin:import/typescript",
"plugin:prettier/recommended",
"plugin:promise/recommended",
"prettier",
"prettier/@typescript-eslint"
],
"rules": {
"mocha/no-exclusive-tests": "error",
"strict": "error"
}
},
"eslintIgnore": [
"lib",
"test/fixtures"
],
"husky": {
"pre-commit": "lint-staged"
},
"lint-staged": {
"*.{json,md,yml}": "prettier --write",
"*.ts": [
"prettier --write",
"eslint --fix"
]
},
"prettier": {
"singleQuote": true
}
}
92 changes: 58 additions & 34 deletions src/Walker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import { NativeModuleType } from './nativeModuleTypes';
export type VersionRange = string;
export interface PackageJSON {
name: string;
dependencies: { [name: string]: VersionRange }
devDependencies: { [name: string]: VersionRange }
optionalDependencies: { [name: string]: VersionRange }
dependencies: { [name: string]: VersionRange };
devDependencies: { [name: string]: VersionRange };
optionalDependencies: { [name: string]: VersionRange };
}
export interface Module {
path: string;
depType: DepType;
nativeModuleType: NativeModuleType,
nativeModuleType: NativeModuleType;
name: string;
}

Expand All @@ -34,11 +34,13 @@ export class Walker {
this.rootModule = modulePath;
}

private relativeModule(rootPath: string, moduleName: string) {
private relativeModule(rootPath: string, moduleName: string): string {
return path.resolve(rootPath, 'node_modules', moduleName);
}

private async loadPackageJSON(modulePath: string): Promise<PackageJSON | null> {
private async loadPackageJSON(
modulePath: string
): Promise<PackageJSON | null> {
const pJPath = path.resolve(modulePath, 'package.json');
if (await fs.pathExists(pJPath)) {
const pJ = await fs.readJson(pJPath);
Expand All @@ -50,12 +52,19 @@ export class Walker {
return null;
}

private async walkDependenciesForModuleInModule(moduleName: string, modulePath: string, depType: DepType) {
private async walkDependenciesForModuleInModule(
moduleName: string,
modulePath: string,
depType: DepType
): Promise<void> {
let testPath = modulePath;
let discoveredPath: string | null = null;
let lastRelative: string | null = null;
// Try find it while searching recursively up the tree
while (!discoveredPath && this.relativeModule(testPath, moduleName) !== lastRelative) {
while (
!discoveredPath &&
this.relativeModule(testPath, moduleName) !== lastRelative
) {
lastRelative = this.relativeModule(testPath, moduleName);
if (await fs.pathExists(lastRelative)) {
discoveredPath = lastRelative;
Expand All @@ -67,7 +76,11 @@ export class Walker {
}
}
// If we can't find it the install is probably buggered
if (!discoveredPath && depType !== DepType.OPTIONAL && depType !== DepType.DEV_OPTIONAL) {
if (
!discoveredPath &&
depType !== DepType.OPTIONAL &&
depType !== DepType.DEV_OPTIONAL
) {
throw new Error(
`Failed to locate module "${moduleName}" from "${modulePath}"

Expand All @@ -80,26 +93,36 @@ export class Walker {
}
}

private async detectNativeModuleType(modulePath: string, pJ: PackageJSON): Promise<NativeModuleType> {
private async detectNativeModuleType(
modulePath: string,
pJ: PackageJSON
): Promise<NativeModuleType> {
if (pJ.dependencies['prebuild-install']) {
return NativeModuleType.PREBUILD
return NativeModuleType.PREBUILD;
} else if (await fs.pathExists(path.join(modulePath, 'binding.gyp'))) {
return NativeModuleType.NODE_GYP
return NativeModuleType.NODE_GYP;
}
return NativeModuleType.NONE
return NativeModuleType.NONE;
}

private async walkDependenciesForModule(modulePath: string, depType: DepType) {
private async walkDependenciesForModule(
modulePath: string,
depType: DepType
): Promise<void> {
d('walk reached:', modulePath, ' Type is:', DepType[depType]);
// We have already traversed this module
if (this.walkHistory.has(modulePath)) {
d('already walked this route');
// Find the existing module reference
const existingModule = this.modules.find(module => module.path === modulePath) as Module;
const existingModule = this.modules.find(
(module) => module.path === modulePath
) as Module;
// If the depType we are traversing with now is higher than the
// last traversal then update it (prod superseeds dev for instance)
if (depTypeGreater(depType, existingModule.depType)) {
d(`existing module has a type of "${existingModule.depType}", new module type would be "${depType}" therefore updating`);
d(
`existing module has a type of "${existingModule.depType}", new module type would be "${depType}" therefore updating`
);
existingModule.depType = depType;
}
return;
Expand Down Expand Up @@ -127,13 +150,15 @@ export class Walker {
// npm decides it's a funny thing to put optional dependencies in the "dependencies" section
// after install, because that makes perfect sense
if (moduleName in pJ.optionalDependencies) {
d(`found ${moduleName} in prod deps of ${modulePath} but it is also marked optional`);
d(
`found ${moduleName} in prod deps of ${modulePath} but it is also marked optional`
);
continue;
}
await this.walkDependenciesForModuleInModule(
moduleName,
modulePath,
childDepType(depType, DepType.PROD),
childDepType(depType, DepType.PROD)
);
}

Expand All @@ -142,44 +167,43 @@ export class Walker {
await this.walkDependenciesForModuleInModule(
moduleName,
modulePath,
childDepType(depType, DepType.OPTIONAL),
childDepType(depType, DepType.OPTIONAL)
);
}

// For every dev dep, but only if we are in the root module
if (depType === DepType.ROOT) {
d('we\'re still at the beginning, walking down the dev route');
d("we're still at the beginning, walking down the dev route");
for (const moduleName in pJ.devDependencies) {
await this.walkDependenciesForModuleInModule(
moduleName,
modulePath,
childDepType(depType, DepType.DEV),
childDepType(depType, DepType.DEV)
);
}
}
}

private async uncachedWalkTree(): Promise<Module[]> {
this.modules = [];
await this.walkDependenciesForModule(this.rootModule, DepType.ROOT);
return this.modules;
}

private cache: Promise<Module[]> | null = null;
async walkTree() {
async walkTree(): Promise<Module[]> {
d('starting tree walk');
if (!this.cache) {
this.cache = new Promise<Module[]>(async (resolve, reject) => {
this.modules = [];
try {
await this.walkDependenciesForModule(this.rootModule, DepType.ROOT);
} catch (err) {
reject(err);
return;
}
resolve(this.modules);
});
this.cache = this.uncachedWalkTree();
} else {
d('tree walk in progress / completed already, waiting for existing walk to complete');
d(
'tree walk in progress / completed already, waiting for existing walk to complete'
);
}
return await this.cache;
}

public getRootModule() {
public getRootModule(): string {
return this.rootModule;
}
}
20 changes: 14 additions & 6 deletions src/depTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ export enum DepType {
DEV,
OPTIONAL,
DEV_OPTIONAL,
ROOT
ROOT,
}

export const depTypeGreater = (newType: DepType, existing: DepType) => {
export const depTypeGreater = (
newType: DepType,
existing: DepType
): boolean => {
switch (existing) {
case DepType.DEV:
switch (newType) {
Expand Down Expand Up @@ -65,11 +68,16 @@ export const depTypeGreater = (newType: DepType, existing: DepType) => {
default:
return false;
}
}
};

export const childDepType = (parentType: DepType, childType: DepType) => {
export const childDepType = (
parentType: DepType,
childType: DepType
): DepType => {
if (childType === DepType.ROOT) {
throw new Error('Something went wrong, a child dependency can\'t be marked as the ROOT');
throw new Error(
"Something went wrong, a child dependency can't be marked as the ROOT"
);
}
switch (parentType) {
case DepType.ROOT:
Expand All @@ -85,4 +93,4 @@ export const childDepType = (parentType: DepType, childType: DepType) => {
if (childType === DepType.OPTIONAL) return DepType.DEV_OPTIONAL;
return DepType.DEV;
}
}
};
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from './Walker';
export * from './depTypes';
export * from './depTypes';
2 changes: 1 addition & 1 deletion src/nativeModuleTypes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export enum NativeModuleType {
NONE,
NODE_GYP,
PREBUILD
PREBUILD,
}
Loading