Skip to content
This repository has been archived by the owner on Sep 1, 2022. It is now read-only.

Commit

Permalink
refactor: rewrite and prepare for release (#34)
Browse files Browse the repository at this point in the history
* build: yarn berry

* build: babel over tsc

* refactor: rewrite rewrite rewrite

* refactor: pretty, types

* chore: explicitly set yarn (#35)

* build: add type cmd

* refactor: fix typing

* wip: args

* ci: node 14

* ci: remove semantic release in ci

* test: stubs

* build: remove ts-jest

* wip: node<14 compat

* wip: node<14 compat

* refactor: use structUtils to stringify ident

* build: precommit

* build: notype on commit

* wip: add filtering for relatives

* ci: run types in ci

* refactor: report type

* wip: report printing

* wip: ignore builtins

* chore: minimatch for glob matching

* wip: cliargs, includelist

* build: add prepack

* chore: erroneous reporting of func used before defined

* refactor: extract diffing

* ci: explicit runs-on

* test: high-level cov

* refactor: split off utils

* chore: remove test dir

* test: monorep case, test utils

* docs: stub

* chore: husky cleanup

* wip: support for include and config file

* refactor: relocate tests

* wip: add exclude option, config f

* chore: ignore test types

* chore: dead code

* chore: add plugin-npm, pack

* wip: add fixer

Co-authored-by: Noah <[email protected]>
  • Loading branch information
mcataford and Noah authored Mar 2, 2021
1 parent 9da33da commit edef28c
Show file tree
Hide file tree
Showing 44 changed files with 11,376 additions and 8,527 deletions.
8 changes: 6 additions & 2 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
module.exports = {
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint", "prettier"],
extends: [
"plugin:@typescript-eslint/recommended",
"@tophat/eslint-config/base",
"@tophat/eslint-config/jest",
"prettier"
],
rules: {
'@typescript-eslint/member-delimiter-style': 0
'@typescript-eslint/member-delimiter-style': 0,
'@typescript-eslint/no-use-before-define': ["error", { functions: false }]
},
settings: {
"import/resolver": {
"node": {
"extensions": [".ts", ".js"]
}
},
typescript: { alwaysTryTypes: true }
}
}
}
42 changes: 14 additions & 28 deletions .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,31 @@ on: [push]
jobs:
lint:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: 12
node-version: 14
- run: yarn
- run: yarn lint

test:
types:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: 14
- run: yarn
- run: yarn types
test:
strategy:
matrix:
node-version: [10.x, 12.x]
node-version: [12.x, 14.x]

runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
Expand All @@ -38,33 +46,11 @@ jobs:

build:
runs-on: ubuntu-latest

needs: [lint, test]

steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: 12
node-version: 14
- run: yarn
- run: yarn build

release:
name: Release
runs-on: ubuntu-latest
needs: [build, lint, test]
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Node setup
uses: actions/setup-node@v1
with:
node-version: 12
- name: Prepare
run: yarn && yarn build
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: yarn semantic-release --dry-run
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,15 @@ node_modules
*.swp
lib
*.tgz
*.sw*
# Yarn
.yarn/*
!.yarn/releases
!.yarn/plugins
!.yarn/versions
!.yarn/sdks
.pnp.*

# IDE
.vim
.vscode
1 change: 1 addition & 0 deletions .husky/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
_
4 changes: 4 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

yarn lint-staged
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v14.15.5
842 changes: 842 additions & 0 deletions .yarn/releases/yarn-sources.cjs

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions .yarn/sdks/eslint/bin/eslint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env node

const {existsSync} = require(`fs`);
const {createRequire, createRequireFromPath} = require(`module`);
const {resolve} = require(`path`);

const relPnpApiPath = "../../../../.pnp.cjs";

const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);

if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require eslint/bin/eslint.js
require(absPnpApiPath).setup();
}
}

// Defer to the real eslint/bin/eslint.js your application uses
module.exports = absRequire(`eslint/bin/eslint.js`);
6 changes: 6 additions & 0 deletions .yarn/sdks/eslint/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "eslint",
"version": "6.8.0-pnpify",
"main": "./lib/api.js",
"type": "commonjs"
}
6 changes: 6 additions & 0 deletions .yarn/sdks/integrations.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# This file is automatically generated by PnPify.
# Manual changes will be lost!

integrations:
- vscode
- vim
30 changes: 30 additions & 0 deletions .yarn/sdks/prettier/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env node

const {existsSync} = require(`fs`);
const {createRequire, createRequireFromPath} = require(`module`);
const {resolve, dirname} = require(`path`);

const relPnpApiPath = "../../../.pnp.cjs";

const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);

if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require prettier/index.js
require(absPnpApiPath).setup();
}

const pnpifyResolution = require.resolve(`@yarnpkg/pnpify`, {paths: [dirname(absPnpApiPath)]});
if (typeof global[`__yarnpkg_sdk_is_using_pnpify__`] === `undefined`) {
Object.defineProperty(global, `__yarnpkg_sdk_is_using_pnpify__`, {configurable: true, value: true});

process.env.NODE_OPTIONS += ` -r ${pnpifyResolution}`;

// Apply PnPify to the current process
absRequire(pnpifyResolution).patchFs();
}
}

// Defer to the real prettier/index.js your application uses
module.exports = absRequire(`prettier/index.js`);
6 changes: 6 additions & 0 deletions .yarn/sdks/prettier/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "prettier",
"version": "2.2.1-pnpify",
"main": "./index.js",
"type": "commonjs"
}
20 changes: 20 additions & 0 deletions .yarn/sdks/typescript/bin/tsc
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env node

const {existsSync} = require(`fs`);
const {createRequire, createRequireFromPath} = require(`module`);
const {resolve} = require(`path`);

const relPnpApiPath = "../../../../.pnp.cjs";

const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);

if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require typescript/bin/tsc
require(absPnpApiPath).setup();
}
}

// Defer to the real typescript/bin/tsc your application uses
module.exports = absRequire(`typescript/bin/tsc`);
20 changes: 20 additions & 0 deletions .yarn/sdks/typescript/bin/tsserver
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env node

const {existsSync} = require(`fs`);
const {createRequire, createRequireFromPath} = require(`module`);
const {resolve} = require(`path`);

const relPnpApiPath = "../../../../.pnp.cjs";

const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);

if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require typescript/bin/tsserver
require(absPnpApiPath).setup();
}
}

// Defer to the real typescript/bin/tsserver your application uses
module.exports = absRequire(`typescript/bin/tsserver`);
6 changes: 6 additions & 0 deletions .yarn/sdks/typescript/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "typescript",
"version": "3.9.9-pnpify",
"main": "./lib/typescript.js",
"type": "commonjs"
}
1 change: 1 addition & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
yarnPath: .yarn/releases/yarn-sources.cjs
1 change: 1 addition & 0 deletions .yvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.22.10
25 changes: 13 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,34 @@

---

## Motivation
## A tale of well-defined dependencies

Because of [how NodeJS import resolution functions](https://nodejs.org/api/modules.html#modules_all_together), it is possible for packages that are not part of your project's `package.json` to the resolved by `import` and `require` statements in your code. This obviously isn't ideal because these "ghost dependencies" operate outside of the contract between your project's dependencies and whatever system installs and runs it.
Because of [how NodeJS import resolution works](https://nodejs.org/api/modules.html#modules_all_together), it is possible for packages that are not part of your project's `package.json` to be resolved by `import` and `require` statements in your code. This creates a "works on machine" scenario where your code depends on packages installed outside of their respective projects, causing failures for whoever consumes your packages and does not have the same global installs.

Having these ghostly imports can cause a whole lot of issues, ranging from cases of "it works on my machine!" to static analysis tools failing to detect circular imports between groups of packages (if your project is a monorepo). Of course, best practices dictate that anything imported that isn't a core package should be listed in `package.json`, but sometimes best practices are overlooked and messes happen. This is where `ghost-imports-buster` comes in.

`ghost-imports-buster` compiles a list of your peer, development and production dependencies (based on `package.json`) and then traverses your package's source code to get an equivalent list of anything that is imported. It then diffs those two lists (with some exclusions, because relative/absolute file imports and core packages don't need policing) to verify that any third-party library that is used in your application code is accounted for in the package configuration.
With the `ghost-imports-buster` dependency validator, you can monitor dependencies declared in your `package.json` files and compare them against what is actually imported in your code. It then becomes easy to find and eliminate extraneous dependencies (which are declared, but not used anywhere) and fix ghost dependencies (which are not declared, but imported in code).

## Installation

```
yarn add ghost-imports-buster -D
```

or
## Usage

```
npm add ghost-imports-buster --save-dev
yarn ghost-imports-buster [--cwd <cwd>] [--include <inclusion glob>]
```

## Usage
## Configuration

```
yarn ghost-imports-buster validate <project root>
```
You can pass in parameters to the ghost import call to customize the analysis:

Note that the project root should coincide with the location of your project's `package.json` file.
|Parameter|Type|Description|
|---|---|---|
|`cdw`|`string`|Directory root to execute the validation from, defaults to `.`|
|`include`|`string`|Glob to filter files to include when looking for imports, can be used multiple times to define multiple globs. Defaults to `**/*`|
|`exclude`|`string`|Glob to filter files to exclude when looking for imports. Same usage as `include`|
|`fix`|`boolean`|When set, unused dependencies are removed and undeclared ones, added. This adds `latest` if available.|

## Contributing

Expand Down
15 changes: 15 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: { node: '14.15.5' },
},
],
'@babel/preset-typescript',
],
plugins: [
'@babel/plugin-proposal-nullish-coalescing-operator',
'@babel/plugin-proposal-optional-chaining',
],
}
22 changes: 10 additions & 12 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
module.exports = {
"roots": [
"<rootDir>/src"
],
testMatch: [
"**/__tests__/**/*.+(ts|tsx|js)",
"**/?(*.)+(spec|test).+(ts|tsx|js)"
],
"transform": {
"^.+\\.(ts|tsx)?$": "ts-jest"
},
"collectCoverage": true,
"coverageDirectory": "./coverage"
roots: ['<rootDir>/tests'],
testMatch: [
'**/__tests__/**/*.+(ts|tsx|js)',
'**/?(*.)+(spec|test).+(ts|tsx|js)',
],
transform: {
'^.+\\.(ts|tsx)?$': 'babel-jest',
},
collectCoverage: true,
coverageDirectory: './coverage',
}
Loading

0 comments on commit edef28c

Please sign in to comment.