Set up a convenient npm project for yourself and the team or a little about modern front-end tools


    Hello. Recently, I came across a task to configure the traffic of private npm packages. Everything sounded very interesting and promising until it turned out that there was not much to do there. It would all be over, but the second task arose - to write a demo repository for the npm package, which could be taken, cloned and quickly created on its base something useful and in the same style.


    The result was a project with customized formatting, code type, tests for each pool, code coverage limits, code coverage report and automatic documentation. Plus convenient publication in npm. Details about the setting - under the cut.


    Requirements


    At first, I figured that we already have:


    • New projects are written in TypeScript
    • In addition to new projects, there are a lot of projects in pure JavaScript
    • There are requirements to write tests and the results should be sent for analysis.

    Then he figured out his Wishlist - since there is time and desire, why not go for a walk. What I want more:


    • I want a uniform formatting style
    • I want a single style TypeScript
    • I want documentation, but I don't want to write it
    • In general, I want to automate everything to the maximum. What would fyr-fyr-fyr and in production

    As a result, the requirements were formed in the following:


    • The module must be TypeScript and verified using TsLint
    • The module must be used from under TypeScript and from under JavaScript
    • Tests should be configured to git hook, minimum code coverage should also be configured, statistics should be
    • Formatting must be configured
    • Documentation must be created from code.
    • The publication should be comfortable and uniform.
    • All that can be automated

    It seems to be good, you need to try.


    Preliminary gestures


    We create (we clone) a repository, we initialize package.json, we put locally TypeScript. In general, all the dependencies set locally, because everything will go to the server. Do not forget to fix version dependencies .


    git init
    npm init
    npm i -D typescript
    ./node_modules/.bin/tsc --init

    Immediately on the spot, you need to correct tsconfig.json for yourself - set the target, libs, include / exclude, outDir and other options.


    Formatting style


    To preserve the uniformity of formatting, I took two tools. The first is the .editorconfig file. It is understood by all major IDEs (WebStorm, VSCode, Visual Studio, etc.), does not require installation of anything extra and works with a large number of file types - js, ts, md, and so on.


    #root = true
    [*]
    indent_style = space
    end_of_line = lf
    charset = utf-8
    trim_trailing_whitespace = true
    insert_final_newline = true
    max_line_length = 100
    indent_size = 4
    [*.md]
    trim_trailing_whitespace = false

    Now auto-formatting will behave more or less the same for me and for my colleagues.


    The second tool is the prettier . This is an npm package that checks, and if possible, automatically adjusts your text formatting. Install it locally and add the first command to package.json


    npm i -D prettier

    package.json


    "prettier": "prettier--config.prettierrc.json--writesrc/**/*.ts"

    The prettier has no init command, so you need to configure it manually. Create a .prettierrc.json at the root of the project with roughly such controversial content (if anything, the post is not at all about which quotes are better, but you can try)


    .prettierrc.json


    {
        "tabWidth": 4,
        "useTabs": false,
        "semi": true,
        "singleQuote": true,
        "trailingComma": "es5",
        "arrowParens": "always"
    }

    Now create the src folder, create index.ts in it with some conditional content and try to run prettier. If he doesn’t like your formatting, he will fix it automatically. Very comfortably. If you do not want to remember this only during a commit / push, you can configure it to autorun or install an extension for the studio.


    Code style


    With the style of the code is also not difficult. For JavaScript there is eslint , for TypeScript there is tslint . Put tslint and create tsconfig.js to store settings


    npm i -D tslint
    ./node_modules/.bin/tslint --init

    package.json


    "lint": "tslint -c tslint.json 'src/**/*.ts' 'tests/**/*.spec.ts'"

    You can write your own rules, or you can use already existing rules using the extend parameter. Here , for example, a config from airbnb.


    Tslint developers are joking
    module.exports = {
        extends: "./tslint-base.json",
        rules: {
            "no-excessive-commenting": [true, {maxComments: Math.random() * 10}]
        }
    };

    Isn't it beautiful?


    There is an important point - tslint and prettier intersect along the functional (for example, in the length of a line or "hanging" commas). So, if you use both - you will need to monitor compliance or abandon something.


    And yet, for those who want to check not all files, but only staged - there is a lint-staged package


    Tests


    What do we need from the tests first? First, that they run automatically, secondly, the control of coverage, thirdly, some kind of report, preferably in the lcov format (if that - lcov is well understood by different analyzers - from SonarQube to CodeCov). Automation, we will do a bit later, while we set up the tests themselves.


    We put karma , jasmine and all the corresponding kit


    npm i -D karma karma-jasmine jasmine karma-typescript karma-chrome-launcher @types/jasmine
    ./node_modules/.bin/karma init

    We modify karma.conf.js a bit and immediately set up the coated operation


    karma.conf.js
    karmaTypescriptConfig : {
        include: ["./src/**/*.ts", "./tests/**/*.spec.ts"],
        tsconfig: "./tsconfig.json",
        reports: {
            "html": "coverage",
            "lcovonly": {
                directory: './coverage',
                filename: '../lcov.dat'
            }
        },
        coverageOptions: {
            threshold: {
                global: {
                    statements: 60,
                    branches: 60,
                    functions: 60,
                    lines: 60,
                    excludes: []
                },
                file: {
                    statements: 60,
                    branches: 60,
                    functions: 60,
                    lines: 60,
                    excludes: [],
                    overrides: {}
                }
            }
        },
    }

    And, of course, do not forget to add a new command in package.json


    package.json


    "test": "karma start"

    If you are using or planning to use CI, then it is better to put the HeadlessChrome :


    npm i -D puppeteer

    And preconfigure Karma (Chrome fix on ChromeHeadless) plus something else. Edits can be viewed in the demo repository .


    Run the tests, check that everything works. At the same time, check the coverage report (it is in the coverage folder) and remove it from version control, it is not needed in the repository.


    Report:



    Commit style


    Commits can also be unified (and at the same time bring someone to a white-hot, if you overdo it, so it's better without fanaticism). For this, I took commitizen . It works in the form of a dialogue, supports the conventional-changelog format (you can create a changelog from its commits) and there is a VsCode plugin for it


    npm i -D commitizen
    npm i -D cz-conventional-changelog

    cz-conventional-changelog is an adapter that is responsible for the questions, and as a result, for the format of your commits


    Add a new command to the scripts section


    "commit":"git-cz"

    And the new section package.json - config for commitizen


    "config": {
            "commitizen": {
                "path": "./node_modules/cz-conventional-changelog"
            }
        }

    The dialogue with commitizen looks like this:



    Documentation


    Now to the documentation. We will have two kinds of documentation - from the code and from the commits. For documentation from the code, I took typedoc (similar to esdoc but for TypeScript). It is very easy to install and work. The main thing do not forget to remove the results of his labors from the control of versions.


    npm i typedoc -D

    and update package.json


    package.json


    "doc": "typedoc --out docs/src/ --readme ./README.md"

    The --readme flag will force the readme to be included in the main documentation page, which is convenient.


    The second type of documentation is the changelog, and the conventional-changelog-cli package will help us .


    npm i -D conventional-changelog-cli

    package.json


    "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s"

    From angular there is only formatting and nothing else. Now, in order to update the changelog, just run npm run changelog. The main thing is to write commits carefully. Well, we always write perfect commits, so this should not be a problem.


    Bild


    Since our package should work for JavaScript, we need to turn TypeScript into JavaScript. In addition, it would be nice to make a minified bundle, just in case. To do this, we need uglifyjs and tweak package.json a bit


    npm i -D uglifyjs

    package.json


    "clean":"rmdir dist /S /Q",
    "build": "tsc --p ./ --sourceMap false",
    "bundle": "uglifyjs ./dist/*.js --compress --mangle --output ./dist/index.min.js"

    By the way, if you want to control the size of your project, there are two more interesting packages.



    They can also be integrated into the process of kommit / push / public list in order to keep in the acceptable size of the bundle. Very, very helpful.


    Automation


    Well, we have already done the basic steps, now everything needs to be automated, otherwise it will be frankly inconvenient to work.


    For this we need another package - husky . It rewrites git hooks and calls the associated commands from package.json. For example, when you make a commit, precommit, push - prepush, and so on will work. If the script returns an error, the commit will crash.


    npm i -D husky

    package.json


    "precommit":"npm run prettier",
    "prepush": "call npm run lint && call npm run test"

    There is an important nuance, the use of call syntax is not cross-platform and will not take off on unix systems. So if you want to do everything in an honest way, you will also have to install the npm-run-all package, it does the same thing but cross-platform.


    Publication


    Well, we have come to the publication of our (albeit empty) package. Let's think what we want from the publication?


    • Test everything again
    • Build build artifacts
    • Collect documentation
    • Raise version
    • Push tags
    • Send to npm

    Hands do it all - sad. Either you forget something, or you need to write a checklist. It is also necessary to automate. You can put another package - unleash . And you can use the native hooks of the npm itself - preversion, version, postversion, for example like this:


    "preversion": "npm run test",
    "version": "call npm run clean && call npm run build && npm run bundle && call npm run doc && call npm run changelog && git add . && git commit -m 'changelogupdate'--no-verify","postversion": "git add . && git push && git push --tags",

    It remains to specify for package.json what to include in the package, entry point and path to our types (do not forget to specify --declaration flag in tsconfig.json to get d.ts files)


    package.json


    "main": "./dist/index.min.js",
    "types": "./dist/index.d.ts",
    "files": [
            "dist/",
            "src/",
            "tests/"
        ]

    Well, that seems to be all. Now it’s enough to execute


    npm version ...
    npm publish

    And everything else will be done automatically.


    Bonus


    As a bonus, there is a demo repository that all supports + CI using travis-ci. Let me remind you that HeadlessChrome is configured there, so if this is important to you, I advise you to look there.


    Acknowledgments


    Many thanks to Alexey Volkov for his report on JsFest, which became the basis of this article.


    Thanks max7z , indestructible , justboris for clarifying that the paths to local dependencies can be omitted.


    And some statistics


    • Size of node_modules: 444 MB (NTFS)
    • Number of dependencies of the first level: 17
    • Total number of used packages: 643

    useful links



    Also popular now: