Giter Club home page Giter Club logo

tiny-invariant's Introduction

tiny-invariant 🔬💥

GitHub Actions Workflow Status types npm bundle size NPM Downloads

tiny-invariant is a tiny, widely-supported, zero-dependency alternative to invariant.

tiny-invariant - when every byte counts!

What is invariant?

An invariant function takes a value, and if the value is falsy then the invariant function will throw. If the value is truthy, then the function will not throw.

import invariant from 'tiny-invariant';

invariant(truthyValue, 'This should not throw!');

invariant(falsyValue, 'This will throw!');
// Error('Invariant violation: This will throw!');

Why tiny-invariant?

The library: invariant supports passing in arguments to the invariant function in a sprintf style (condition, format, a, b, c, d, e, f). It has internal logic to execute the sprintf substitutions. The sprintf logic is not removed in production builds. tiny-invariant has dropped all of the code for sprintf logic and instead encourages consumers to leverage template literals for message formatting.

invariant(condition, `Hello, ${name} - how are you today?`);

Error Messages

tiny-invariant allows you to pass a string message, or a function that returns a string message. Using a function that returns a message is helpful when your message is expensive to create.

import invariant from 'tiny-invariant';

invariant(condition, `Hello, ${name} - how are you today?`);

// Using a function is helpful when your message is expensive
invariant(value, () => getExpensiveMessage());

When process.env.NODE_ENV is set to production, the message will be replaced with the generic message Invariant failed.

Type narrowing

tiny-invariant is useful for correctly narrowing types for flow and typescript

const value: Person | null = { name: 'Alex' }; // type of value == 'Person | null'
invariant(value, 'Expected value to be a person');
// type of value has been narrowed to 'Person'

API: (condition: any, message?: string | (() => string)) => void

  • condition is required and can be anything
  • message optional string or a function that returns a string (() => string)

Installation

# yarn
yarn add tiny-invariant

# npm
npm install tiny-invariant --save

Dropping your message for kb savings!

Big idea: you will want your compiler to convert this code:

invariant(condition, 'My cool message that takes up a lot of kbs');

Into this:

if (!condition) {
  if ('production' !== process.env.NODE_ENV) {
    invariant(false, 'My cool message that takes up a lot of kbs');
  } else {
    invariant(false);
  }
}

Your bundler can then drop the code in the "production" !== process.env.NODE_ENV block for your production builds to end up with this:

if (!condition) {
  invariant(false);
}

Builds

  • We have a es (EcmaScript module) build
  • We have a cjs (CommonJS) build
  • We have a umd (Universal module definition) build in case you needed it

We expect process.env.NODE_ENV to be available at module compilation. We cache this value

That's it!

🤘

tiny-invariant's People

Contributors

alexreardon avatar andarist avatar birdhighway avatar dependabot[bot] avatar greenkeeper[bot] avatar itsdouges avatar iyegoroff avatar madou avatar mdibyo avatar mlaopane avatar ngryman avatar pkerschbaum avatar svicalifornia avatar trysound avatar vipulbhj avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tiny-invariant's Issues

Error: Invariant failed -> In Remix project

Hi,

I'm facing this error during Github Action CI, can someone please advice?

> start:mocks
> binode --require ./mocks -- @remix-run/serve:remix-serve build

🔶 Mock server running

/home/runner/work/project-name/tproject-namenode_modules/tiny-invariant/dist/tiny-invariant.cjs.js:10
        throw new Error(prefix);
              ^
Error: Invariant failed
    at invariant (/home/runner/work/project-name/project-name/node_modules/tiny-invariant/dist/tiny-invariant.cjs.js:10:15)
    at Object.<anonymous> (/home/runner/work/project-name/project-name/build/index.js:124:35)
    at Module._compile (node:internal/modules/cjs/loader:1126:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1180:10)
    at Module.load (node:internal/modules/cjs/loader:1004:32)
    at Function.Module._load (node:internal/modules/cjs/loader:839:12)
    at Module.require (node:internal/modules/cjs/loader:1028:19)
    at require (node:internal/modules/cjs/helpers:102:18)
    at Object.<anonymous> (/home/runner/work/project-name/project-name/node_modules/@remix-run/serve/dist/cli.js:48:13)
    at Module._compile (node:internal/modules/cjs/loader:1126:14)
http://localhost:8811 timed out on retry 91 of 3, elapsed 90246ms, limit 90000ms
Error: connect ECONNREFUSED 127.0.0.1:8811

Any thoughts on the above?

Thanks a lot!

Fails to import as default in serverless env.

I have a ts serverless repo with the following tsconfig.json:

{
  "compilerOptions": {
    "strict": true,
    "preserveConstEnums": true,
    "strictNullChecks": true,
    "sourceMap": true,
    "allowJs": true,
    "target": "es5",
    "outDir": ".build",
    "moduleResolution": "node",
    "lib": ["es2015"],
    "rootDir": ".",
    "baseUrl": ".",
    "paths": {
      "test-utils/*": ["tests/utils/*"],
      "~/*": ["src/*"]
    },
    "resolveJsonModule": true
  }
}

When I import tiny-invariant with its default import (import invariant from 'tiny-invariant') , my serverless crashes:

TypeError: (0 , tiny_invariant_1.default) is not a function

But if I import it using named import (import * as invariant from 'tiny-invariant'), it works fine except now my vscode starts complaining:

This expression is not callable.
  Type 'typeof import(".../node_modules/tiny-invariant/dist/tiny-invariant")' has no call signatures.ts(2349)

Is there something wrong with my configuration?

idea: extra param for more details

In dev mode, just an error can be a bit terse. I then have to find the failing assertion and add a bunch of console.log about the current state.

I could include the data in the error message, but:

  • it's not good for privacy
  • it's not good for matching the error in logs

How about an extended API assert(digits.startsWith(ref_digits), 'xxx() ref_digits must match', () => console.error({digits, ref_digits}))

What do you think?

Why do we not log messages in production?

Hi

Why are we not logging the messages in prod?

When process.env.NODE_ENV is set to production, the message will be replaced with the generic message Invariant failed.

How do you guys figure out what went wrong in prod?

Incorrect Types when using in a TypeScript ESM project

🕗 Version & Regression Information

1.2.0

⏯ Playground Link

https://stackblitz.com/edit/node-3rxg4d

  1. Run npm install.
  2. Run tsc compilation via npm run build. The error occurs.

💻 Code

import invariant from 'tiny-invariant';

invariant(typeof 'foo' === 'string');

🙁 Actual behavior

I tried to run the code above from a TypeScript ESM package, this means:

  • "type": "module" set in package.json
  • "module": "node16" set in tsconfig.json

But in such a project, the code does not compile:

❯ npm run build
$ tsc
src/test.ts:3:1 - error TS2349: This expression is not callable.
  Type 'typeof import("/home/projects/node-3rxg4d/node_modules/tiny-invariant/dist/tiny-invariant")' has no call signatures.

invariant(typeof 'foo' === 'string');

Found 1 error in src/test.ts:3

Note that the compiled output does run as expected, so the only problem is the TypeScript compilation error.

🙂 Expected behavior

The code should compile.

Provide repository to package.json for WebJars compatibility

I am trying to use tiny-invariant with webjars.org and get the following error:

The metadata was missing a required field: /repository/url
This could be easily fixed by adding the information to the package.json:

"repository": {
  "type" : "git",
  "url" : "..."
}

Thanks in advance!

More meaningful name for `invariant` function

I would like to have a more meaningful name for the function than invariant. Something like throwOnFalsy is for me easier to understand.

While working with developers that don’t know what tiny-invariant is, I realized that is not easy understand at first sight what the invariant function does. To improve this, it would be nice to rename the function with something more descriptive. I would still keep the original function to avoid breaking changes but also allow to import the new function with the descriptive name.

What do you think? Is throwOnFalsy good for you or maybe you have a better name?
If you think it is a good idea, I would submit a PR for that.

Move to Typescript

Right now we are sort of blocked given that there needs to be no way to do an invariant style guard

Feature: Auto debugger

This is actually a feature request. I would of course be glad to be directed toward some existing solution.

I very often want to debug via browser devtools when an invariant fails. I do this one of two ways

  1. Set a conditional breakpoint on the invariant where the condition is the inverse of the invariant. This is not always possible/easy if the invariant is nontrivial. I also often forget to remove this breakpoint and have to do so later.
  2. Temporarily refactory/copy my invariant like so
  // invariant(val)
  if (!val) { debugger }

I've never been able to rely on exception breakpoints (caught or uncaught) due to e.g. React.

I'd love some sort of ability to have my invariants automatically open a debugger. Either configured globally (invariant.debugOnFailure = true) or per instance (invariant.debug(val))

tiny-invariant throws on an empty string

I use https://github.com/fram-x/assert-ts#check-condition for my simple code assertions, which allows me to narrow string | null | undefined to string w/o throwing an error on an empty string. tiny-invariant differs in that - is that intentional?

I find it confusing, since "" is still a string, but it does more clearly follow the if(!condition) pattern more directly, though you can achieve the same w/ assert-ts with assert(!!possiblyNullishOrEmptyString) to cause it to throw on an empty string

codesandbox.io/s/assert-v-s-tiny-invariant-z2w4gq

An in-range update of rollup is breaking the build 🚨

The devDependency rollup was updated from 1.9.2 to 1.9.3.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

rollup is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • continuous-integration/travis-ci/push: The Travis CI build could not complete due to an error (Details).

Release Notes for v1.9.3

2019-04-10

Bug Fixes

  • Simplify return expressions that are evaluated before the surrounding function is bound (#2803)

Pull Requests

  • #2803: Handle out-of-order binding of identifiers to improve tree-shaking (@lukastaegert)
Commits

The new version differs by 3 commits.

  • 516a06d 1.9.3
  • a5526ea Update changelog
  • c3d73ff Handle out-of-order binding of identifiers to improve tree-shaking (#2803)

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Exported as `.default` in CJS build

At present when requiring this library from CJS code (e.g. in Node), require('tiny-invariant') returns an object and you have to access the function from the .default property.

const invariant = require('tiny-invariant').default;

I know the purpose of this module is primarily for client-side code as then it can be effectively minimised in production builds. However, I like it so much, I now use it everywhere including pure Node.js code!

Would you consider reconfiguring the CJS builds so require('tiny-invariant') returns the actual invariant function?

To avoid breaking changes, it'd be possible to also export invariant as property .default of itself e.g.:

// Compiled CJS build
function invariant(condition, message) { /* ... */ }
invariant.default = invariant;
module.exports = invariant;

Or, to also issue a deprecation warning:

const warning = require('tiny-warning');
Object.defineProperty(invariant, 'default', {
  get() {
    warning(
      false,
      'Accessing `invariant` as `require('tiny-invariant').default` is deprecated.\n'
      + "Use `require('tiny-invariant')` (without `.default` instead)."
    );
    return invariant;
  }
});

How do you feel about this?

I'd be happy to submit a PR.

How to show error message in production?

On production I noticed that it only says "Error: Invariant failed" but it doesn't explain what the error is about.
What is the best practice for investigating error with tiny-invariant on the server?

Version 10 of node.js has been released

Version 10 of Node.js (code name Dubnium) has been released! 🎊

To see what happens to your code in Node.js 10, Greenkeeper has created a branch with the following changes:

  • Added the new Node.js version to your .travis.yml

If you’re interested in upgrading this repo to Node.js 10, you can open a PR with these changes. Please note that this issue is just intended as a friendly reminder and the PR as a possible starting point for getting your code running on Node.js 10.

More information on this issue

Greenkeeper has checked the engines key in any package.json file, the .nvmrc file, and the .travis.yml file, if present.

  • engines was only updated if it defined a single version, not a range.
  • .nvmrc was updated to Node.js 10
  • .travis.yml was only changed if there was a root-level node_js that didn’t already include Node.js 10, such as node or lts/*. In this case, the new version was appended to the list. We didn’t touch job or matrix configurations because these tend to be quite specific and complex, and it’s difficult to infer what the intentions were.

For many simpler .travis.yml configurations, this PR should suffice as-is, but depending on what you’re doing it may require additional work or may not be applicable at all. We’re also aware that you may have good reasons to not update to Node.js 10, which is why this was sent as an issue and not a pull request. Feel free to delete it without comment, I’m a humble robot and won’t feel rejected 🤖


FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Accept generator function for error message

For dev-time error messages (and for those not using build-time rewriters), it would be nice if time-consuming calculations for error messages were only performed in the event of an error. This would be easy to achieve with minimal bloat using a lambda:

invariant(something, () => `my message with ${complex()} calculations`)

The build-time rewriters will be able to strip this with no changes necessary.

Internally:

throw new Error(`${prefix}: ${typeof message == 'function' ? message() : message || ''}`);

question: how (should I) to replace a message with just a code in production

Hi,
This is more of a question for best practice - I am writing a library for the first time, and couldnt find much information and would like to hear your thoughts.

Both tiny-invariant and tiny-warning and working as expected - and they are indeed shaving off quite nice chunks of kbs from production build.

However, when some errors (in client apps that are using my library) do slip through development phase and happen in production, the error thrown as expected is just a generic invarianat failed with a stack trace pointing to the library.

Is there a mechanism wherby I could at least introduce/keep some error code i.e. E343 instead of a whole message being stripped away for production?
For ex, in development builds the message would be:
"E343: You forgot to wrap the components with the top level provider. Please ensure x, y, z"

And in production it would be just "E343?"

Does that make sense? I tried to investigate some of your libraries to see how you use them, but for example in react-beautiful-dnd, besides tiny-invariant, it seems you also have a more customized version "dev-warning.js" - Is dev-warning just early iteration before you came up with tiny-warning? Can you maybe provide some general guidelines/best practices on using these two for library authors and any gotchas you experiences?

CusomError

hi,

it would be nice if you can pass, as second parameter, a custom error to be thrown.

e.g:

invariant(currentUser.roles.includes("admin"), new ForbiddenError("You are not authorized to view this resource"));

Fabio.

consider adding `warning` as well?

Hey, thanks for this awesome library!

I was wondering if you'd consider adding the warning (source) counterpart to Facebook's invariant that logs warnings instead of throwing errors? It would be awesome to be able to use a single, tiny library for this like:

import { invariant, warning } from 'tiny-invariant'

Or maybe it would be in another?

import invariant from 'tiny-invariant'
import warning from 'tiny-warning'

They're both super useful for reducing build sizes I've found.

Thanks!

Disable message dropping behavior

I am getting errors without any context in prod. I assume this is because of the message dropping behavior. How can I disable this?

What could a new tiny-invariant API look like? (Custom errors, prefixes, aliases etc)

A big goal of this library is to be as lite as possible. Yet, there are some common and useful features that would be great for this library to have:

  • a message can be a string or lazy (eg with a function for when your error message is expensive to compute)
  • The Error that is thrown should be able to be any Error (eg MyCustomError) or perhaps even any value (you might want to throw a promise for example) #166
  • "Invariant failed: " prefix should be optional (or perhaps removed completely - do we even need a prefix?) #143
  • I think this library should could to named imports only to reduce consumption (would require a codemode). Alternatively could just add two named exports (invariant and assert)
  • We could add an assert named export (#153)

All of these features are possible. I am trying to think through:

  • What would a nice™️ look like (I am currently thinking the second argument might need to be a string or an options object)
  • How to achieve the above outcomes without weighing down people for features that they do not want to use? (a main goal of tiny-invariant is to be tiny)

v1.3.0: TypeScript issue when importing `tiny-invariant` from an CJS codebase and `"moduleResolution"` is set to `"node16"`

🕗 Version & Regression Information

1.3.0

⏯ Playground Link

https://stackblitz.com/edit/node-cwmfq6

  1. Run npm install.
  2. Run tsc compilation via npm run build. The error occurs.

Problem

If tiny-invariant is used from an TypeScript CJS package with moduleResolution node16, this means:

  • "type" in package.json is not set or set to commonjs
  • and "moduleResolution": "node16" set in tsconfig.json

...then TypeScript cannot find the types:

❯ npm run build
$ tsc
src/test.ts:1:23 - error TS7016: Could not find a declaration file for module 'tiny-invariant'. '/home/projects/node-cwmfq6/node_modules/tiny-invariant/dist/tiny-invariant.cjs.js' implicitly has an 'any' type.
  Try `npm i --save-dev @types/tiny-invariant` if it exists or add a new declaration (.d.ts) file containing `declare module 'tiny-invariant';`

1 import invariant from 'tiny-invariant';
                        ~~~~~~~~~~~~~~~~


Found 1 error in src/test.ts:1

Reason

See #150.

An in-range update of @rollup/plugin-typescript is breaking the build 🚨


☝️ Important announcement: Greenkeeper will be saying goodbye 👋 and passing the torch to Snyk on June 3rd, 2020! Find out how to migrate to Snyk and more at greenkeeper.io


The devDependency @rollup/plugin-typescript was updated from 3.0.0 to 3.1.0.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

@rollup/plugin-typescript is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • continuous-integration/travis-ci/push: The Travis CI build failed (Details).

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.