Exporting my own Flow type with npm package?
Asked Answered
T

4

7

I have a npm package of React components which are using flow for type-checking.

It would be useful for the users of my components to have access to my flow types. However at the moment I am compiling my code using Babel which strips all type information.

My project structure is as follows :

|
|- flowdecls
       myTypes.js
| -components
    - Component1
        Component1.js
| - lib
    - Component1.js (compiled using Babel)
    - Component1.js.flow (created using flow-copy-source)


For example one of my types in myTypes.js is

declare type DataItemIconType = {
    iconElement: React$Element<React$ElementType>,
    color?: string,
    hoverColor?: string
}

which I would using in Component1. For example one of the props of Component1 would be

iconList : Array<DataItemIconType>

I have already published several versions of my library of React components as an npm package without Flow and my components are being widely used. However I would really like to provide flow support.

In my most recent I tried using flow-copy-source as specified in this article (Authoring and publishing JavaScript modules with Flow) but users of my library still can't access my types.

How would I make a type such as DataItemIconType available to someone using Component1 in my library ?

package.json

{
  "name": "@company/react-common-components-build-template",
  "version": "0.6.0",
  "main": "./lib/index.js",
  "private": true,
  "engines": {
    "node": ">=4.0.0"
  },

  "files": [
    "lib"
  ],
  "description": "Common component library",
  "peerDependencies": {
    "react": "16.10.0",
    "react-dom": "16.10.0",
    "prop-types": "15.7.2"
  },
  "dependencies": {
    "@material-ui/core": "4.9.5",
    "@material-ui/icons": "4.9.1",
    "@material-ui/lab": "3.0.0-alpha.30",
    "@material-ui/styles": "4.9.0",
    "lodash": "4.17.15"
  },
  "devDependencies": {
    "@babel/cli": "7.4.4",
    "@babel/core": "7.6.0",
    "@babel/node": "7.2.2",
    "@babel/plugin-proposal-class-properties": "7.2.1",
    "@babel/plugin-syntax-dynamic-import": "7.2.0",
    "@babel/plugin-transform-object-assign": "7.2.0",
    "@babel/plugin-transform-react-constant-elements": "7.6.0",
    "@babel/plugin-transform-runtime": "7.6.2",
    "@babel/preset-env": "7.4.2",
    "@babel/preset-flow": "7.0.0",
    "@babel/preset-react": "7.0.0",
    "@babel/register": "7.0.0",
    "@svgr/webpack": "4.3.2",
    "@typescript-eslint/eslint-plugin": "^2.2.0",
    "@typescript-eslint/parser": "^2.2.0",
    "babel-eslint": "10.0.3",
    "babel-jest": "24.9.0",
    "babel-loader": "8.0.6",
    "babel-plugin-named-asset-import": "0.3.4",
    "babel-plugin-react-remove-properties": "0.3.0",
    "babel-preset-react-app": "9.0.2",
    "camelcase": "^5.2.0",
    "case-sensitive-paths-webpack-plugin": "2.2.0",
    "chokidar": "1.6.1",
    "classnames": "2.2.6",
    "cpx": "1.5.0",
    "cross-env": "6.0.3",
    "css-loader": "2.1.1",
    "dotenv": "6.2.0",
    "dotenv-expand": "5.1.0",
    "enzyme": "3.10.0",
    "enzyme-adapter-react-16": "1.15.1",
    "enzyme-to-json": "3.4.3",
    "eslint": "6.6.0",
    "eslint-config-react-app": "5.0.2",
    "eslint-loader": "3.0.0",
    "eslint-plugin-flowtype": "3.13.0",
    "eslint-plugin-flowtype-errors": "4.1.0",
    "eslint-plugin-import": "2.18.2",
    "eslint-plugin-jsx-a11y": "6.2.3",
    "eslint-plugin-react": "7.16.0",
    "eslint-plugin-react-hooks": "^2.3.0",
    "file-loader": "3.0.1",
    "flow-bin": "0.113.0",
    "flow-copy-source": "^2.0.9",
    "flow-typed": "^2.6.2",
    "fs-extra": "7.0.1",
    "glob-gitignore": "1.0.14",
    "hard-source-webpack-plugin": "^0.13.1",
    "highlight": "^0.2.4",
    "highlight.js": "^9.10.0",
    "html-webpack-plugin": "4.0.0-beta.5",
    "husky": "3.0.8",
    "identity-obj-proxy": "3.0.0",
    "is-wsl": "^1.1.0",
    "jest": "24.9.0",
    "jest-environment-jsdom-fourteen": "1.0.1",
    "jest-enzyme": "^7.1.2",
    "jest-resolve": "24.9.0",
    "jest-watch-typeahead": "0.4.0",
    "mini-css-extract-plugin": "0.9.0",
    "npm-run-all": "4.0.2",
    "optimize-css-assets-webpack-plugin": "5.0.3",
    "pnp-webpack-plugin": "1.5.0",
    "postcss-flexbugs-fixes": "4.1.0",
    "postcss-loader": "3.0.0",
    "postcss-normalize": "7.0.1",
    "postcss-preset-env": "6.7.0",
    "postcss-safe-parser": "4.0.1",
    "prettier": "1.19.1",
    "react": "16.10.0",
    "react-addons-test-utils": "15.5.1",
    "react-app-polyfill": "^1.0.3",
    "react-dev-utils": "10.2.0",
    "react-docgen": "3.0.0",
    "react-dom": "16.10.0",
    "react-highlight": "^0.12.0",
    "react-test-renderer": "16.10.0",
    "resolve": "1.15.0",
    "resolve-url-loader": "3.1.1",
    "sass-loader": "8.0.2",
    "semver": "6.3.0",
    "style-loader": "1.0.0",
    "terser-webpack-plugin": "2.3.4",
    "ts-pnp": "1.1.5",
    "url-loader": "2.3.0",
    "webpack": "4.41.5",
    "webpack-dev-server": "3.10.2",
    "webpack-manifest-plugin": "2.2.0",
    "workbox-webpack-plugin": "4.3.1"
  },
  "scripts": {
    "prestart": "npm run gen:docs",
    "start": "npm-run-all --parallel start:docs gen:docs-watch",
    "start:docs": "node scripts/start.js",
    "gen:docs": "node scripts/generateComponentData.js",
    "gen:docs-watch": "npm run gen:docs -- --watch",
    "build:docs": "node scripts/build.js",
    "test": "node scripts/test.js",
    "predeploy:docs": "npm run build:docs",
    "flow": "flow",
    "lint": "eslint src --debug",
    "lint:flow-typed": "flow-typed install --ignoreDeps dev",
    "build:images": "cpx \"./src/components/images/**/*.*\" ./lib/images",
    "prebuild:common-components-lib": "rimraf lib",
    "build:common-components-lib": "npm-run-all --parallel build:components build:utils build:images build:copy-files build:copyflowsource",
    "build:components": "cross-env NODE_ENV=production BABEL_ENV=cjs babel ./src/components --out-dir ./lib/ --ignore spec.js",
    "build:utils": "cross-env NODE_ENV=production BABEL_ENV=cjs babel src/components/utils --out-dir ./lib/utils --ignore spec.js",
    "build:copyflowsource": "flow-copy-source ./src/components ./lib ",
    "build:copy-files": "node scripts/copyBuildFiles.js",
    "prettier:changed": "node ./scripts/prettier.js",
    "prettier:all": "node ./scripts/prettier.js write",
    "format-check": "prettier --check \"./src/**/*.{js,test.js,spec.js}\""
  },
  "publishConfig": {
    "registry": "http://srv-ie-nexus/repository/npm-hosted/"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "jest": {
    "roots": [
      "<rootDir>/src/components"
    ],
    "collectCoverageFrom": [
      "src/**/*.{js,jsx,ts,tsx}",
      "!src/**/*.d.ts"
    ],
    "setupFiles": [
      "react-app-polyfill/jsdom"
    ],
    "setupFilesAfterEnv": [
      "<rootDir>/jest-test-setup.js"
    ],
    "testMatch": [
      "<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}",
      "<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}"
    ],
    "testEnvironment": "jest-environment-jsdom-fourteen",
    "transform": {
      "^.+\\.(js|jsx|ts|tsx)$": "<rootDir>/node_modules/babel-jest",
      "^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
      "^(?!.*\\.(js|jsx|ts|tsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
    },
    "transformIgnorePatterns": [
      "[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$",
      "^.+\\.module\\.(css|sass|scss)$"
    ],
    "modulePaths": [],
    "moduleNameMapper": {
      "^react-native$": "react-native-web",
      "^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy"
    },
    "moduleFileExtensions": [
      "web.js",
      "js",
      "web.ts",
      "ts",
      "web.tsx",
      "tsx",
      "json",
      "web.jsx",
      "jsx",
      "node"
    ],
    "watchPlugins": [
      "jest-watch-typeahead/filename",
      "jest-watch-typeahead/testname"
    ]
  }
}
Temperature answered 19/5, 2020 at 11:31 Comment(9)
Did you make the changes in package.json? How are your users trying to access your flow types? There is an alternative solution, I think, but I'd have to try it outSimdars
Yes I included the changes in my package.json scripts to call flow-copy-source as part of my build to end up with what I have in lib above. My users would be using the types as a guide / help of the structure in which they should supply data into my components. For example a user of Component1 (which could be something like a table) would be supply icon data for each table row using DataItemIconType. It would be useful to them to show how they should supply the element and that it can have a color and hoverColor.Temperature
Can you show your package.jsonSimdars
Updated question with package.jsonTemperature
@Simdars - Any further ideas ?Temperature
Sorry Simon! Didn't get a chance to look!Simdars
I did notice another Flow package that published their types, but did it separately. I'm still convinced that your way should work, however.Simdars
@Simdars - No problem. Just curious. Have asked some of my users to test this solution to see how it goes.Temperature
It's a weirdly busy time for me, given there's a pandemic. I tried to look at your package until I realised it was private. (But published to a private registry?) I would suggest downloading the tarball for your package and checking to see if the flow files are there. npm ignores files in your .gitignore when it bundles. That would be my only other idea without firing it up and trying it myselfSimdars
C
1

Flow will automatically look for adjecent module.js.flow declarations files, if present.

For example, if you have a main field that points to dist/index.js, then adding dist/index.js.flow to your package's outputs will have it picked up by the user's language server.

Cedeno answered 7/7, 2020 at 5:15 Comment(1)
This is true but I don't think this is exactly what OP is asking for. The question to my understanding is more about what if the library has their own declaration files that aren't part of source code but instead part of libs (in flowdecls dir)Validity
V
1

I don't think flow has a solution to this yet. But I was in the same boat as you.

Pretty much the reason your consumers aren't able to use your package types are because they read global types from their .*/flow-typed/* not your one which when they install your package will be in .*/node_modules/@company/react-common-components-build-template. It has nothing to do with any package.json settings.

You have two options though to ship your package with types.

Option 1:

Either you put them as part of your src code and allow them to import it, which means your types won't be global anymore and you need to import the types you want to use within your source code. But this means so can you consumers.

Option 2:

Or keep them in your flowdecls and ensure they're published. Once they are, your users will have to copy your type defs into their flow-typed dir, which will now make them global within their project also.

You can add something like the following into your README to give users some insight:


react-common-components-build-template also uses global type definitions internally. If you would like to take advantage of this, you can copy /node_modules/@company/react-common-components-build-template/flowdecls/myTypes.js to your local flow-typed directory. It's probably a good idea to do a copy once per version upgrade so you stay in-sync with the latest changes which you can do via npm scripts.

"scripts": {
  // ...
  "postinstall": "cp /node_modules/@company/react-common-components-build-template/flowdecls/myTypes.js flow-typed"
},

The second option is actually what I've gone with, and the above is what I have in my repo. As a side note, I would recommend though that you rename your dir to flow-typed and your types file to something like react-common-components-build-template.js so that when the copy process happens, people don't wonder what myTypes.js is used for.

Validity answered 4/6, 2020 at 4:37 Comment(1)
Excellent answer. Exactly the kind of advice and options I have been looking for. Many thanks.Temperature
C
1

Flow will automatically look for adjecent module.js.flow declarations files, if present.

For example, if you have a main field that points to dist/index.js, then adding dist/index.js.flow to your package's outputs will have it picked up by the user's language server.

Cedeno answered 7/7, 2020 at 5:15 Comment(1)
This is true but I don't think this is exactly what OP is asking for. The question to my understanding is more about what if the library has their own declaration files that aren't part of source code but instead part of libs (in flowdecls dir)Validity
T
0

This looks to be a very useful package as part of this process also - https://www.npmjs.com/package/gen-flow-files

Temperature answered 1/7, 2020 at 9:18 Comment(0)
C
0

For that you can use:

  • flow-remove-types NPM package which will create lib/Component1.js without flow types. Or do it with Babel if you want
  • flow-copy-source NPM package which will copy flow typed files to the destination folder with the .flow suffix lib/Component1.js.flow
Clift answered 27/10, 2020 at 7:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.