npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

project-lint

v0.0.4

Published

A CLI tool to lint project structure

Downloads

85

Readme

Project Lint

A small tool for setting up project structure linting configuration and running checks to ensure all files fit the requirements.

The code was inspired by the dead project (MIT License)

Features

  • Validation of project structure (Any files/folders outside the structure will be considered an error).
  • Name case validation.
  • Name regex validation.
  • Inheriting the parent's name (The child inherits the name of the folder in which it is located).
  • Folder recursion (You can nest a given folder structure recursively).
  • Forcing a nested/flat structure for a given folder.
  • Ignoring by pattern or via .gitignore.

To Do

  • Sub-folder level .projectlintrc.
  • URL based extends.
  • Default extends.
  • Updatable regexpPatterns.

Installation

$ npm i project-lint

Usage

Run

$ project-lint -c ./project-structure.yml "./**/*"

Help

$ project-lint -h
Usage: project-lint [options] [pathnames...]

File Structure Validator

Arguments:
  pathnames                   Paths to the files to validate

Options:
  -v, --version               Current project-linter version
  -c, --config <path>         Path to the config file (default: "./.projectlintrc")
  -i, --ignore <patterns...>  Ignore patterns (default: ["node_modules/**"])
  -d, --debug [level]         output extra debugging (default: 0)
  -s, --minimalistic          Minimalistic mode
  -h, --help                  display help for command

Lint Staged

module.export = {
  '**/*': ['project-lint -s'],
};

Examples

Simple example for the structure below:

.
├── ...
├── 📄 .projectlintrc
├── 📄 .eslintrc.json
└── 📂 src
    ├── 📄 index.tsx
    └── 📂 components
        ├── ...
        └── 📄 ComponentName.tsx
root:
  - name: .projectlintrc
    type: file
  - name: .eslintrc.json
    type: file
  - name: src
    type: dir
    children:
      - name: index.tsx
        type: file
      - name: components
        type: dir
        children:
          - name: /^${{PascalCase}}\.tsx$/
            type: file

Advanced example for the structure below, containing all key features:

.
├── ...
├── 📄 .gitignore (contains node_modules and etc...)
├── 📄 .projectlintrc
├── 📄 .eslintrc.json
└── 📂 src
    ├── 📂 legacy
    │   ├── ...
    ├── 📂 hooks
    │   ├── ...
    │   ├── 📄 useSimpleGlobalHook.test.ts
    │   ├── 📄 useSimpleGlobalHook.ts
    │   └── 📂 useComplexGlobalHook
    │       ├── 📁 hooks (recursion)
    │       ├── 📄 useComplexGlobalHook.api.ts
    │       ├── 📄 useComplexGlobalHook.types.ts
    │       ├── 📄 useComplexGlobalHook.test.ts
    │       └── 📄 useComplexGlobalHook.ts
    └── 📂 components
        ├── ...
        └── 📂 ParentComponent
            ├── 📄 parentComponent.api.ts
            ├── 📄 parentComponent.types.ts
            ├── 📄 ParentComponent.context.tsx
            ├── 📄 ParentComponent.test.tsx
            ├── 📄 ParentComponent.tsx
            ├── 📂 components
            │   ├── ...
            │   └── 📂 ChildComponent
            │       ├── 📁 components (recursion)
            │       ├── 📁 hooks (recursion)
            │       ├── 📄 childComponent.types.ts
            │       ├── 📄 childComponent.api.ts
            │       ├── 📄 ChildComponent.context.tsx
            │       ├── 📄 ChildComponent.test.tsx
            │       └── 📄 ChildComponent.tsx
            └── 📂 hooks
                ├── ...
                ├── 📄 useSimpleParentComponentHook.test.ts
                ├── 📄 useSimpleParentComponentHook.ts
                └── 📂 useComplexParentComponentHook
                    ├── 📁 hooks (recursion)
                    ├── 📄 useComplexParentComponentHook.api.ts
                    ├── 📄 useComplexParentComponentHook.types.ts
                    ├── 📄 useComplexParentComponentHook.test.ts
                    └── 📄 useComplexParentComponentHook.ts
$schema: ./node_modules/project-lint/projectlintrc.schema.json
workdir: .
gitignore: true

extends: ./node_modules/project-lint/extends/rules.yml

ignorePatterns:
  - src/legacy

root:
  - ruleId: basic_root
  - name: src
    type: dir
    children:
      - ruleId: hooks_folder
      - ruleId: components_folder

rules:
  basic_root:
    - ruleId: projectlint_root
    - ruleId: git_root
    - ruleId: eslint_root
    - ruleId: webpack_root
    # ... etc, check in `extends` file

  hooks_folder:
    - name: hooks
      type: dir
      children:
        - name: /^use${{PascalCase}}(\.(test|api|types))?\.ts$/
          type: file
        - name: /^use${{PascalCase}}$/
          type: dir
          children:
            - ruleId: hooks_folder # recursion
            - name: /^${{parentName}}(\.(test|api|types))?\.ts$/
              type: file

  component_folder:
    - name: /^${{PascalCase}}$/
      type: dir
      children:
        - name: /^${{parentName}}\.(api|types)\.tsx$/
          type: file
        - name: /^${{ParentName}}(\.(context|test))?\.tsx$/
          type: file
        - ruleId: hooks_folder # recursion
        - ruleId: components_folder # recursion

  components_folder:
    - name: components
      type: dir
      children:
        - ruleId: component_folder

API:

$schema: <string> (optional)

Type checking for your '.projectlintrc'. It helps to fill configuration correctly.

{
  "$schema": "./node_modules/project-lint/projectlintrc.schema.json",
  // ...
}

workdir: <string> (default: .)

Set workdir related to config file.

gitignore: <boolean> (default: false)

Include .gitignore file to ignorePatterns.

ignorePatterns: <string[]> (optional)

.gitignore syntaxed list of files to ignore when running.

ignorePatterns:
  - src/legacy

extends: <string | string[]> (optional)

List of other YAML or JSON files with the rules. It will be recursively included and overridden by the current file. You may extend rules, ignorePatterns, but it won't include gitignore: true.

root: <Rule[]>

Rules of the root of the project (related to workdir).

rules: <{ [ruleId]: Rule[] }>

List of named rules may be used via ruleId rule. It may include itself recursively.

Rules

Named file

File rule must contain 2 fields: name and type: file.

name: <string | RegExp>

- name: filename.ts
  type: file
- name: /^file${{PascalCase}}\.ts$/
  type: file

Named folder/dir

Folder rule must contain 2 fields: name and type: file. All the included content may be described in other 2 fields: ignoreChildren or children.

name: <string | RegExp>

- name: dirname
  type: dir
- name: /^dir${{PascalCase}}\.ts$/
  type: dir

ignoreChildren: <boolean>

All the included files will pass successfully.

- name: dirname
  type: dir
  ignoreChildren: true

children: <Rule[]>

All content describes as regular.

allowAny: <boolean>

All not described contents in folder will be ignored. It is useful when we need to ignore a single level but need to check described folders.

root:
  - name: src
    type: dir
    children:
      # rules...

  - allowAny: true

ruleId: <ruleId>

To get any of described in rules section.

root:
  - ruleId: test

rules:
  test:
    - name: src
      type: dir
      ignoreChildren: true

Built-in regex parameters

${{PascalCase}}

Define a pascal case in RegExp name.

- name: /^use${{PascalCase}}\.ts$/
  type: file

Expecting any file called like useMyHook.ts.

${{camelCase}}

Define a camel case in RegExp name.

- name: /^${{camelCase}}Service\.ts$/
  type: file

Expecting any file called like myApiService.ts.

${{kebab-case}} (alias ${{dash-case}})

Define a kebab case in RegExp name.

- name: /^${{kebab-case}}\.d\.ts$/
  type: file

Expecting any file called like module-federation.d.ts.

${{snake_case}}

Define a snake case in RegExp name.

- name: /^${{snake_case}}\.css$/
  type: file

Expecting any file called like i_am_beautiful.css.

${{parentName}}

Use the same name as the parent folder, but the first letter must be lowercase.

- name: TheGodfather
  type: dir
  children:
    - name: /^${{parentName}}\.component\.tsx$/
      type: file
- name: theShavka
  type: dir
  children:
    - name: /^${{parentName}}\.test\.tsx$/
      type: file

Will expect the following:

.
├── 📂 TheGodfather
│   ├── 📄 theGodfather.component.ts
└── 📂 theShavka
    └── 📄 theShavka.test.ts

${{ParentName}}

Use the same name as the parent folder, but the first letter must be uppercase.

- name: Batman
  type: dir
  children:
    - name: /^${{ParentName}}\.must-suffer\.asm$/
      type: file
- name: robin
  type: dir
  children:
    - name: /^${{ParentName}}\.stay-strong\.yaml$/
      type: file

Will expect the following:

.
├── 📂 Batman
│   ├── 📄 Batman.must-suffer.asm
└── 📂 robin
    └── 📄 Robin.stay-strong.yaml

Configuration file

It is YAML by default and must be called .projectlintrc. But you may use json syntax instead.

Contrubution

The more MB of a bundle we have -- the more features we deliver. It works that way, I believe.