From 4c31068e06aed18067472e8957bd09b572e281b0 Mon Sep 17 00:00:00 2001 From: Douglas Armstrong Date: Tue, 31 May 2022 16:57:31 -0700 Subject: [PATCH] Update build scripts for recoil-relay (#1799) Summary: Pull Request resolved: https://github.com/facebookexperimental/Recoil/pull/1799 Update package build scripts for `recoil-relay` Reviewed By: mondaychen Differential Revision: D36429965 fbshipit-source-id: 60f984dcc987ae3468a24a563be225bbdcc644b2 --- .gitignore | 2 +- CHANGELOG-recoil-relay.md | 2 +- CHANGELOG-recoil-sync.md | 2 +- CHANGELOG-recoil.md | 4 +- CHANGELOG-refine.md | 2 +- package.json | 11 +- .../recoil-relay/package-for-release.json | 18 + packages/recoil-sync/package-for-release.json | 4 +- packages/recoil/package-for-release.json | 10 +- relay.config.js | 6 +- scripts/build.mjs | 209 +++---- scripts/pack.mjs | 26 + scripts/rollup-configs.mjs | 119 ++-- scripts/utils.mjs | 18 + typescript/index.d.ts | 575 +---------------- typescript/{tests.ts => recoil-test.ts} | 0 typescript/recoil.d.ts | 585 ++++++++++++++++++ typescript/tsconfig.json | 6 +- 18 files changed, 802 insertions(+), 797 deletions(-) create mode 100644 packages/recoil-relay/package-for-release.json create mode 100644 scripts/pack.mjs create mode 100644 scripts/utils.mjs rename typescript/{tests.ts => recoil-test.ts} (100%) create mode 100644 typescript/recoil.d.ts diff --git a/.gitignore b/.gitignore index eef5f067c..ff8f85f49 100644 --- a/.gitignore +++ b/.gitignore @@ -94,4 +94,4 @@ package-lock.json /docs # Generated files for recoil-relay unit tests -recoil-relay/__tests__/mock-graphql/__generated__ +packages/recoil-relay/__tests__/mock-graphql/__generated__ diff --git a/CHANGELOG-recoil-relay.md b/CHANGELOG-recoil-relay.md index 6951b8dd8..85c1e8d45 100644 --- a/CHANGELOG-recoil-relay.md +++ b/CHANGELOG-recoil-relay.md @@ -3,6 +3,6 @@ ## UPCOMING **_Add new changes here as they land_** -## 0.1.0 (2022-05) +## 0.1.0 (2022-06) Initial open source release diff --git a/CHANGELOG-recoil-sync.md b/CHANGELOG-recoil-sync.md index 6951b8dd8..85c1e8d45 100644 --- a/CHANGELOG-recoil-sync.md +++ b/CHANGELOG-recoil-sync.md @@ -3,6 +3,6 @@ ## UPCOMING **_Add new changes here as they land_** -## 0.1.0 (2022-05) +## 0.1.0 (2022-06) Initial open source release diff --git a/CHANGELOG-recoil.md b/CHANGELOG-recoil.md index 97c21a6e9..31789edf9 100644 --- a/CHANGELOG-recoil.md +++ b/CHANGELOG-recoil.md @@ -3,8 +3,10 @@ ## UPCOMING **_Add new changes here as they land_** +## 0.7.3 (2022-06-01) + - Atom effects can initialize or set atoms to wrapped values (#1681) -- Add `parentStoreID_UNSTABLE` to atom effects. This will match up with the store that the current store was cloned from, such as the host `` store for `useRecoilCallback()` snapshots. (#1744) +- Add `parentStoreID_UNSTABLE` to atom effects which is the ID of the parent store it cloned from, such as the host `` store for `useRecoilCallback()` snapshots. (#1744) - Enable atoms and selectors to be used in family parameters (#1740) ## 0.7.2 (2022-04-13) diff --git a/CHANGELOG-refine.md b/CHANGELOG-refine.md index 83be340da..bbcfbf9a1 100644 --- a/CHANGELOG-refine.md +++ b/CHANGELOG-refine.md @@ -5,6 +5,6 @@ # Version numbers based on host Recoil Sync package -## 0.1.0 (2022-05) +## 0.1.0 (2022-06) Initial open source release diff --git a/package.json b/package.json index d0300fe1c..e151a8e5c 100644 --- a/package.json +++ b/package.json @@ -2,10 +2,10 @@ "name": "recoil", "version": "0.7.2", "description": "Recoil - A state management library for React", - "main": "cjs/recoil.js", - "module": "es/recoil.js", - "react-native": "native/recoil.js", - "unpkg": "umd/recoil.js", + "main": "cjs/index.js", + "module": "es/index.js", + "react-native": "native/index.js", + "unpkg": "umd/index.js", "files": [ "umd", "es", @@ -17,8 +17,9 @@ "license": "MIT", "scripts": { "prepare": "install-peers", - "build-all": "node scripts/build.mjs all", "build": "rollup -c && node scripts/postbuild.js", + "build-TMP": "node scripts/build.mjs", + "pack": "node scripts/pack.mjs", "test": "yarn relay && jest packages/*", "format": "prettier --write \"./**/*.{js,md,json}\"", "flow": "flow --show-all-errors", diff --git a/packages/recoil-relay/package-for-release.json b/packages/recoil-relay/package-for-release.json new file mode 100644 index 000000000..86c77b6ab --- /dev/null +++ b/packages/recoil-relay/package-for-release.json @@ -0,0 +1,18 @@ +{ + "name": "recoil-relay", + "version": "0.1.0", + "description": "recoil-relay helps Recoil perform type safe and efficient queries using GraphQL with Relay", + "main": "cjs/index.js", + "module": "es/index.js", + "unpkg": "umd/index.js", + "types": "index.d.ts", + "files": ["umd", "es", "cjs", "index.d.ts"], + "repository": "https://github.com/facebookexperimental/Recoil.git", + "license": "MIT", + "dependencies": {}, + "peerDependencies": { + "recoil": ">=0.7.3", + "react-relay": ">=13.2.0", + "relay-runtime": ">=13.2.0" + } +} diff --git a/packages/recoil-sync/package-for-release.json b/packages/recoil-sync/package-for-release.json index 833b1e319..a9d3d9166 100644 --- a/packages/recoil-sync/package-for-release.json +++ b/packages/recoil-sync/package-for-release.json @@ -1,6 +1,6 @@ { "name": "recoil-sync", - "version": "0.0.1-alpha.0", + "version": "0.1.0", "description": "recoil-sync provides an add-on library to help synchronize Recoil state with external systems", "main": "cjs/index.js", "module": "es/index.js", @@ -13,6 +13,6 @@ "transit-js": "^0.8.874" }, "peerDependencies": { - "recoil": ">=0.7.0" + "recoil": ">=0.7.3" } } diff --git a/packages/recoil/package-for-release.json b/packages/recoil/package-for-release.json index 9a36b7685..c6d10f7fc 100644 --- a/packages/recoil/package-for-release.json +++ b/packages/recoil/package-for-release.json @@ -1,11 +1,11 @@ { "name": "recoil", - "version": "0.7.3-alpha.2", + "version": "0.7.3", "description": "Recoil - A state management library for React", - "main": "cjs/recoil.js", - "module": "es/recoil.js", - "react-native": "native/recoil.js", - "unpkg": "umd/recoil.js", + "main": "cjs/index.js", + "module": "es/index.js", + "react-native": "native/index.js", + "unpkg": "umd/index.js", "types": "index.d.ts", "files": ["umd", "es", "cjs", "native", "index.d.ts"], "repository": "https://github.com/facebookexperimental/Recoil.git", diff --git a/relay.config.js b/relay.config.js index 023dd76e1..3a7771607 100644 --- a/relay.config.js +++ b/relay.config.js @@ -1,6 +1,6 @@ module.exports = { // Configuration options accepted by the `relay-compiler` command-line tool and `babel-plugin-relay`. - src: "./packages/recoil-relay/__tests__", - schema: "./packages/recoil-relay/__tests__/mock-graphql/schema.graphql", - exclude: ["**/node_modules/**", "**/__mocks__/**", "**/__generated__/**"], + src: './packages/recoil-relay/__tests__', + schema: './packages/recoil-relay/__tests__/mock-graphql/schema.graphql', + exclude: ['**/node_modules/**', '**/__mocks__/**', '**/__generated__/**'], } diff --git a/scripts/build.mjs b/scripts/build.mjs index 8ec006ed8..a063663be 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -10,191 +10,126 @@ import {rollup} from 'rollup'; import { - recoilInputOptions, - recoilSyncInputOptions, + createInputOption, createOutputOption, } from './rollup-configs.mjs'; import {exec} from 'child_process'; import * as fs from 'fs'; import {projectRootDir} from './project-root-dir.mjs'; - -const args = process.argv.slice(2); - -if (args[0] === 'all') { - buildAll(); -} else if (args[0] === 'recoil') { - buildRecoil(); -} +import {createErrorHandler} from './utils.mjs'; // constants const BUILD_TARGET = 'build'; -function createErrorHandler(message) { - return err => { - if (err) { - console.error(`${message}\n`); - throw err; - } - }; +const PACKAGES = { + recoil: { + inputFile: 'Recoil_index.js', + umdName: 'Recoil', + builds: { + common: ['cjs', 'es'], + dev: ['umd'], + prod: ['es-browsers', 'umd-prod'], + native: ['native'], + }, + }, + 'recoil-sync': { + inputFile: 'RecoilSync_index.js', + umdName: 'RecoilSync', + builds: { + common: ['cjs', 'es'], + dev: ['umd'], + prod: ['es-browsers', 'umd-prod'], + }, + }, + 'recoil-relay': { + inputFile: 'RecoilRelay_index.js', + umdName: 'RecoilRelay', + builds: { + common: ['cjs', 'es'], + dev: ['umd'], + prod: ['es-browsers', 'umd-prod'], + }, + }, +}; + +const args = process.argv.slice(2); +const target = args[0]; +if (target === 'all' || target == null) { + buildAll(); +} else { + if (PACKAGES[target] == null) { + throw new Error(`Unknown build target ${target}`); + } + buildPackage(target, PACKAGES[target]); } async function buildAll() { console.log('Building all packages...'); - await buildRecoil(); - await buildRecoilSync(); -} - -async function buildRecoil() { - console.log('Building recoil...'); - const createRecoilOutputOptions = type => - createOutputOption(type, 'recoil', 'recoil', 'Recoil'); - await build( - 'recoil (common)', - recoilInputOptions.common, - ['cjs', 'es'].map(createRecoilOutputOptions), - ); - await build( - 'recoil (dev)', - recoilInputOptions.dev, - ['umd'].map(createRecoilOutputOptions), - ); - await build( - 'recoil (prod)', - recoilInputOptions.prod, - ['es-browsers', 'umd-prod'].map(createRecoilOutputOptions), - ); - await build( - 'recoil (native)', - recoilInputOptions.native, - ['native'].map(createRecoilOutputOptions), - ); - - console.log('Copying files...'); - fs.copyFile( - `${projectRootDir}/packages/recoil/package-for-release.json`, - `${BUILD_TARGET}/recoil/package.json`, - fs.constants.COPYFILE_FICLONE, - createErrorHandler('Failed to copy package-for-release.json'), - ); - - fs.copyFile( - `${projectRootDir}/README.md`, - `${BUILD_TARGET}/recoil/README.md`, - fs.constants.COPYFILE_FICLONE, - createErrorHandler('Failed to copy README.md'), - ); - fs.copyFile( - `${projectRootDir}/CHANGELOG-recoil.md`, - `${BUILD_TARGET}/recoil/CHANGELOG.md`, - fs.constants.COPYFILE_FICLONE, - createErrorHandler('Failed to copy CHANGELOG.md'), - ); - fs.copyFile( - `${projectRootDir}/LICENSE`, - `${BUILD_TARGET}/recoil/LICENSE`, - fs.constants.COPYFILE_FICLONE, - createErrorHandler('Failed to copy LICENSE'), - ); - - console.log('Copying index.d.ts for TypeScript support...'); - fs.copyFile( - `${projectRootDir}/typescript/index.d.ts`, - `${BUILD_TARGET}/recoil/index.d.ts`, - fs.constants.COPYFILE_FICLONE, - createErrorHandler('Failed to copy index.d.ts for recoil'), - ); - - console.log('Generating Flow type files...'); - exec( - `npx flow-copy-source packages/recoil ${BUILD_TARGET}/recoil/cjs`, - err => { - createErrorHandler('Failed to copy recoil source files for flow types')( - err, - ); - fs.rename( - `${BUILD_TARGET}/recoil/cjs/Recoil_index.js.flow`, - `${BUILD_TARGET}/recoil/cjs/recoil.js.flow`, - createErrorHandler('Failed to rename recoil.js.flow'), - ); - }, - ); - console.log('Successfully built recoil!'); + for (const [target, config] of Object.entries(PACKAGES)) { + await buildPackage(target, config); + } } -async function buildRecoilSync() { - console.log('Building recoil-sync...'); - - const createRecoilSyncOutputOptions = type => - createOutputOption(type, 'recoil-sync', 'index', 'RecoilSync'); - await build( - 'recoil-sync (common)', - recoilSyncInputOptions.common, - ['cjs', 'es'].map(createRecoilSyncOutputOptions), - ); - await build( - 'recoil-sync (dev)', - recoilSyncInputOptions.dev, - ['umd'].map(createRecoilSyncOutputOptions), - ); - await build( - 'recoil-sync (prod)', - recoilSyncInputOptions.prod, - ['es-browsers', 'umd-prod'].map(createRecoilSyncOutputOptions), - ); +async function buildPackage(target, config) { + console.log(`Building ${target}...`); + for (const [buildType, packageTypes] of Object.entries(config.builds)) { + await buildRollup( + `recoil (${buildType})`, + createInputOption(buildType, target, config.inputFile), + packageTypes.map(type => createOutputOption(type, target, config.umdName))); + } console.log('Copying files...'); fs.copyFile( - `${projectRootDir}/packages/recoil-sync/package-for-release.json`, - `${BUILD_TARGET}/recoil-sync/package.json`, + `${projectRootDir}/packages/${target}/package-for-release.json`, + `${BUILD_TARGET}/${target}/package.json`, fs.constants.COPYFILE_FICLONE, createErrorHandler('Failed to copy package-for-release.json'), ); fs.copyFile( - `${projectRootDir}/README-recoil-sync.md`, - `${BUILD_TARGET}/recoil-sync/README.md`, + `${projectRootDir}/README${target === 'recoil' ? '' : '-' + target}.md`, + `${BUILD_TARGET}/${target}/README.md`, fs.constants.COPYFILE_FICLONE, - createErrorHandler('Failed to copy README.md'), + createErrorHandler(`Failed to copy README-${target}.md`), ); fs.copyFile( - `${projectRootDir}/CHANGELOG-recoil-sync.md`, - `${BUILD_TARGET}/recoil-sync/CHANGELOG.md`, + `${projectRootDir}/CHANGELOG-${target}.md`, + `${BUILD_TARGET}/${target}/CHANGELOG.md`, fs.constants.COPYFILE_FICLONE, - createErrorHandler('Failed to copy CHANGELOG.md'), + createErrorHandler(`Failed to copy CHANGELOG-${target}.md`), ); fs.copyFile( `${projectRootDir}/LICENSE`, - `${BUILD_TARGET}/recoil-sync/LICENSE`, + `${BUILD_TARGET}/${target}/LICENSE`, fs.constants.COPYFILE_FICLONE, createErrorHandler('Failed to copy LICENSE'), ); console.log('Copying index.d.ts for TypeScript support...'); fs.copyFile( - `${projectRootDir}/typescript/recoil-sync.d.ts`, - `${BUILD_TARGET}/recoil-sync/index.d.ts`, + `${projectRootDir}/typescript/${target}.d.ts`, + `${BUILD_TARGET}/${target}/index.d.ts`, fs.constants.COPYFILE_FICLONE, - createErrorHandler('Failed to copy recoil-sync.d.ts for recoil-sync'), + createErrorHandler(`Failed to copy ${target}.d.ts for TypeScript index.d.ts`), ); console.log('Generating Flow type files...'); exec( - `npx flow-copy-source packages/recoil-sync ${BUILD_TARGET}/recoil-sync/cjs`, + `npx flow-copy-source packages/${target} ${BUILD_TARGET}/${target}/cjs`, err => { - createErrorHandler('Failed to copy recoil source files for flow types')( - err, - ); + createErrorHandler('Failed to copy source files for Flow types')(err); fs.rename( - `${BUILD_TARGET}/recoil-sync/cjs/RecoilSync_index.js.flow`, - `${BUILD_TARGET}/recoil-sync/cjs/index.js.flow`, - createErrorHandler('Failed to rename RecoilSync_index.js.flow'), + `${BUILD_TARGET}/${target}/cjs/${config.inputFile}.flow`, + `${BUILD_TARGET}/${target}/cjs/${target}.js.flow`, + createErrorHandler(`Failed to rename ${target}.js.flow`), ); }, ); - console.log('Successfully built recoil-sync!'); + console.log(`Successfully built ${target}!`); } -async function build(name, inputOptions, outputOptionsList) { +async function buildRollup(name, inputOptions, outputOptionsList) { try { // create a bundle const bundle = await rollup(inputOptions); diff --git a/scripts/pack.mjs b/scripts/pack.mjs new file mode 100644 index 000000000..f15da5de6 --- /dev/null +++ b/scripts/pack.mjs @@ -0,0 +1,26 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @emails oncall+recoil + * @format + */ + +import * as child_process from 'child_process'; +import * as fs from 'fs'; +import {createErrorHandler} from './utils.mjs'; + +const BUILD_TARGET = 'build'; + +for (const dir of fs.readdirSync(BUILD_TARGET)) { + if (fs.lstatSync(`${BUILD_TARGET}/${dir}`).isDirectory()) { + console.log(`Packaging ${JSON.stringify(dir)}`); + child_process.exec( + 'yarn pack', + {cwd: `${BUILD_TARGET}/${dir}`}, + createErrorHandler(`Failed to package ${dir}`), + ); + } +} diff --git a/scripts/rollup-configs.mjs b/scripts/rollup-configs.mjs index 2b79f09c5..6a0d8825a 100644 --- a/scripts/rollup-configs.mjs +++ b/scripts/rollup-configs.mjs @@ -23,6 +23,8 @@ const externalLibs = [ 'react-native', 'recoil', 'transit-js', + 'relay-runtime', + 'react-relay', ]; const defaultNodeResolveConfig = {}; @@ -92,36 +94,80 @@ const productionPlugins = [ terser({mangle: false}), ]; -const outputFolder = 'build'; +const nativePlugins = commonPlugins.map(plugin => + // Replace the default nodeResolvePlugin + plugin !== nodeResolvePlugin ? plugin : + nodeResolve({ + ...defaultNodeResolveConfig, + extensions: ['.native.js', '.js'], + }) +); + +export function createInputOption(buildType, folder, inputFile) { + switch (buildType) { + case 'common': + return { + input: `packages/${folder}/${inputFile}`, + external: externalLibs, + plugins: commonPlugins, + }; + case 'dev': + return { + input: `packages/${folder}/${inputFile}`, + external: externalLibs, + plugins: developmentPlugins, + }; + case 'prod': + return { + input: `packages/${folder}/${inputFile}`, + external: externalLibs, + plugins: productionPlugins, + }; + case 'native': + return { + input: `packages/${folder}/${inputFile}`, + external: externalLibs, + plugins: nativePlugins, + }; + default: + throw new Error(`Unknown input type: ${buildType}`); + } +} + +const BUILD_TARGET = 'build'; + const globals = { react: 'React', 'react-dom': 'ReactDOM', recoil: 'Recoil', 'transit-js': 'transit', + 'relay-runtime': 'relay-runtime', + 'react-relay': 'react-relay', }; -export function createOutputOption(type, folder, filename, UMDName) { - switch (type) { + +export function createOutputOption(packageType, folder, UMDName) { + switch (packageType) { case 'cjs': return { - file: `${outputFolder}/${folder}/cjs/${filename}.js`, + file: `${BUILD_TARGET}/${folder}/cjs/index.js`, format: 'cjs', exports: 'named', }; case 'es': return { - file: `${outputFolder}/${folder}/es/${filename}.js`, + file: `${BUILD_TARGET}/${folder}/es/index.js`, format: 'es', exports: 'named', }; case 'es-browsers': return { - file: `${outputFolder}/${folder}/es/${filename}.mjs`, + file: `${BUILD_TARGET}/${folder}/es/index.mjs`, format: 'es', exports: 'named', }; case 'umd': return { - file: `${outputFolder}/${folder}/umd/${filename}.js`, + file: `${BUILD_TARGET}/${folder}/umd/index.js`, format: 'umd', name: UMDName, exports: 'named', @@ -129,7 +175,7 @@ export function createOutputOption(type, folder, filename, UMDName) { }; case 'umd-prod': return { - file: `${outputFolder}/${folder}/umd/${filename}.min.js`, + file: `${BUILD_TARGET}/${folder}/umd/index.min.js`, format: 'umd', name: UMDName, exports: 'named', @@ -137,64 +183,11 @@ export function createOutputOption(type, folder, filename, UMDName) { }; case 'native': return { - file: `${outputFolder}/${folder}/native/${filename}.js`, + file: `${BUILD_TARGET}/${folder}/native/index.js`, format: 'es', exports: 'named', }; default: - throw new Error(`Unknown output type: ${type}`); + throw new Error(`Unknown output type: ${packageType}`); } } - -const recoilInputFile = 'packages/recoil/Recoil_index.js'; -export const recoilInputOptions = { - common: { - input: recoilInputFile, - external: externalLibs, - plugins: commonPlugins, - }, - dev: { - input: recoilInputFile, - external: externalLibs, - plugins: developmentPlugins, - }, - prod: { - input: recoilInputFile, - external: externalLibs, - plugins: productionPlugins, - }, - native: { - input: recoilInputFile, - external: externalLibs, - plugins: commonPlugins.map(plugin => { - // Replace the default nodeResolve plugin - if (plugin === nodeResolvePlugin) { - return nodeResolve({ - ...defaultNodeResolveConfig, - extensions: ['.native.js', '.js'], - }); - } - - return plugin; - }), - }, -}; - -const recoilSyncInputFile = 'packages/recoil-sync/RecoilSync_index.js'; -export const recoilSyncInputOptions = { - common: { - input: recoilSyncInputFile, - external: externalLibs, - plugins: commonPlugins, - }, - dev: { - input: recoilSyncInputFile, - external: externalLibs, - plugins: developmentPlugins, - }, - prod: { - input: recoilSyncInputFile, - external: externalLibs, - plugins: productionPlugins, - }, -}; diff --git a/scripts/utils.mjs b/scripts/utils.mjs new file mode 100644 index 000000000..a47fbc983 --- /dev/null +++ b/scripts/utils.mjs @@ -0,0 +1,18 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @emails oncall+recoil + * @format + */ + +export function createErrorHandler(message) { + return err => { + if (err) { + console.error(`${message}\n`); + throw err; + } + }; +} diff --git a/typescript/index.d.ts b/typescript/index.d.ts index 0462e5e26..12198d67c 100644 --- a/typescript/index.d.ts +++ b/typescript/index.d.ts @@ -9,577 +9,4 @@ * @emails oncall+recoil */ -/** - * This file is a manual translation of the flow types, which are the source of truth, so we should not introduce new terminology or behavior in this file. - */ - - export { }; - - import * as React from 'react'; - - // state.d.ts - type NodeKey = string; - - // node.d.ts - export class DefaultValue { - private __tag: 'DefaultValue'; - } - - // recoilRoot.d.ts - export type RecoilRootProps = { - initializeState?: (mutableSnapshot: MutableSnapshot) => void, - override?: true, - children: React.ReactNode, - } | { - override: false, - children: React.ReactNode, - }; - - /** - * Root component for managing Recoil state. Most Recoil hooks should be - * called from a component nested in a - */ - export const RecoilRoot: React.FC; - - // Snapshot.d.ts - declare const SnapshotID_OPAQUE: unique symbol; - export interface SnapshotID { - readonly [SnapshotID_OPAQUE]: true; - } - - interface ComponentInfo { - name: string; - } - - interface RecoilStateInfo { - loadable?: Loadable; - isActive: boolean; - isSet: boolean; - isModified: boolean; // TODO report modified selectors - type: 'atom' | 'selector'; - deps: Iterable>; - subscribers: { - nodes: Iterable>, - components: Iterable, - }; - } - - export class Snapshot { - getID(): SnapshotID; - getLoadable(recoilValue: RecoilValue): Loadable; - getPromise(recoilValue: RecoilValue): Promise; - getNodes_UNSTABLE(opts?: { isModified?: boolean, isInitialized?: boolean }): Iterable>; - getInfo_UNSTABLE(recoilValue: RecoilValue): RecoilStateInfo; - map(cb: (mutableSnapshot: MutableSnapshot) => void): Snapshot; - asyncMap(cb: (mutableSnapshot: MutableSnapshot) => Promise): Promise; - retain(): () => void; - isRetained(): boolean; - } - - export class MutableSnapshot extends Snapshot { - set: SetRecoilState; - reset: ResetRecoilState; - } - - declare const WrappedValue_OPAQUE: unique symbol; - export interface WrappedValue { - readonly [WrappedValue_OPAQUE]: true; - } - - // Effect is called the first time a node is used with a - export type AtomEffect = (param: { - node: RecoilState, - storeID: StoreID, - parentStoreID_UNSTABLE?: StoreID, - trigger: 'set' | 'get', - - // Call synchronously to initialize value or async to change it later - setSelf: (param: - | T - | DefaultValue - | Promise - | WrappedValue - | ((param: T | DefaultValue) => T | DefaultValue | WrappedValue), - ) => void, - resetSelf: () => void, - - // Subscribe callbacks to events. - // Atom effect observers are called before global transaction observers - onSet: ( - param: (newValue: T, oldValue: T | DefaultValue, isReset: boolean) => void, - ) => void, - - // Accessors to read other atoms/selectors - getPromise: (recoilValue: RecoilValue) => Promise, - getLoadable: (recoilValue: RecoilValue) => Loadable, - getInfo_UNSTABLE: (recoilValue: RecoilValue) => RecoilStateInfo, - }) => void | (() => void); - - // atom.d.ts - interface AtomOptionsWithoutDefault { - key: NodeKey; - effects?: ReadonlyArray>; - effects_UNSTABLE?: ReadonlyArray>; - dangerouslyAllowMutability?: boolean; - } - interface AtomOptionsWithDefault extends AtomOptionsWithoutDefault { - default: RecoilValue | Promise | Loadable | WrappedValue | T; - } - export type AtomOptions = AtomOptionsWithoutDefault | AtomOptionsWithDefault; - - /** - * Creates an atom, which represents a piece of writeable state - */ - export function atom(options: AtomOptions): RecoilState; - export namespace atom { - function value(value: T): WrappedValue; - } - - export type GetRecoilValue = (recoilVal: RecoilValue) => T; - export type SetterOrUpdater = (valOrUpdater: ((currVal: T) => T) | T) => void; - export type Resetter = () => void; - export interface TransactionInterface_UNSTABLE { - get(a: RecoilValue): T; - set(s: RecoilState, u: ((currVal: T) => T) | T): void; - reset(s: RecoilState): void; - } - export interface CallbackInterface { - set: (recoilVal: RecoilState, valOrUpdater: ((currVal: T) => T) | T) => void; - reset: (recoilVal: RecoilState) => void; // eslint-disable-line @typescript-eslint/no-explicit-any - refresh: (recoilValue: RecoilValue) => void; - snapshot: Snapshot; - gotoSnapshot: (snapshot: Snapshot) => void; - transact_UNSTABLE: (cb: (i: TransactionInterface_UNSTABLE) => void) => void; - } - - // selector.d.ts - export interface SelectorCallbackInterface extends CallbackInterface { - node: RecoilState; // TODO This isn't properly typed - } - export type GetCallback = , Return>( - fn: (interface: SelectorCallbackInterface) => (...args: Args) => Return, - ) => (...args: Args) => Return; - - export type SetRecoilState = ( - recoilVal: RecoilState, - newVal: T | DefaultValue | ((prevValue: T) => T | DefaultValue), - ) => void; - - export type ResetRecoilState = (recoilVal: RecoilState) => void; // eslint-disable-line @typescript-eslint/no-explicit-any - - // export type EqualityPolicy = 'reference' | 'value'; TODO: removing while we discuss long term API - - export type EvictionPolicy = 'lru' | 'keep-all' | 'most-recent'; - - // TODO: removing while we discuss long term API - // export type CachePolicy = - // | {eviction: 'lru', maxSize: number, equality?: EqualityPolicy} - // | {eviction: 'none', equality?: EqualityPolicy} - // | {eviction?: undefined, equality: EqualityPolicy}; - - // TODO: removing while we discuss long term API - // export interface CachePolicyWithoutEviction { - // equality: EqualityPolicy; - // } - - export type CachePolicyWithoutEquality = {eviction: 'lru', maxSize: number} | {eviction: 'keep-all'} | {eviction: 'most-recent'}; - - export interface ReadOnlySelectorOptions { - key: string; - get: (opts: { - get: GetRecoilValue, - getCallback: GetCallback, - }) => Promise | RecoilValue | Loadable | WrappedValue | T; - dangerouslyAllowMutability?: boolean; - cachePolicy_UNSTABLE?: CachePolicyWithoutEquality; // TODO: using the more restrictive CachePolicyWithoutEquality while we discuss long term API - } - - export interface ReadWriteSelectorOptions extends ReadOnlySelectorOptions { - set: ( - opts: { - set: SetRecoilState; - get: GetRecoilValue; - reset: ResetRecoilState; - }, - newValue: T | DefaultValue, - ) => void; - } - - /** - * Creates a selector which represents derived state. - */ - export function selector(options: ReadWriteSelectorOptions): RecoilState; - export function selector(options: ReadOnlySelectorOptions): RecoilValueReadOnly; - export namespace selector { - function value(value: T): WrappedValue; - } - - // hooks.d.ts - - /** - * Returns the value of an atom or selector (readonly or writeable) and - * subscribes the components to future updates of that state. - */ - export function useRecoilValue(recoilValue: RecoilValue): T; - - /** - * Returns a Loadable representing the status of the given Recoil state - * and subscribes the component to future updates of that state. Useful - * for working with async selectors. - */ - export function useRecoilValueLoadable(recoilValue: RecoilValue): Loadable; - - /** - * Returns a tuple where the first element is the value of the recoil state - * and the second is a setter to update that state. Subscribes component - * to updates of the given state. - */ - export function useRecoilState(recoilState: RecoilState): [T, SetterOrUpdater]; - - /** - * Returns a tuple where the first element is a Loadable and the second - * element is a setter function to update the given state. Subscribes - * component to updates of the given state. - */ - export function useRecoilStateLoadable(recoilState: RecoilState): [Loadable, SetterOrUpdater]; - - /** - * Returns a setter function for updating Recoil state. Does not subscribe - * the component to the given state. - */ - - export function useSetRecoilState(recoilState: RecoilState): SetterOrUpdater; - - /** - * Returns a function that will reset the given state to its default value. - */ - export function useResetRecoilState(recoilState: RecoilState): Resetter; // eslint-disable-line @typescript-eslint/no-explicit-any - - /** - * Returns current info about an atom - */ - export function useGetRecoilValueInfo_UNSTABLE(): (recoilValue: RecoilValue) => RecoilStateInfo; - -/** - * Experimental version of hooks for useTransition() support - */ - export function useRecoilValue_TRANSITION_SUPPORT_UNSTABLE(recoilValue: RecoilValue): T; - export function useRecoilValueLoadable_TRANSITION_SUPPORT_UNSTABLE(recoilValue: RecoilValue): Loadable; - export function useRecoilState_TRANSITION_SUPPORT_UNSTABLE(recoilState: RecoilState): [T, SetterOrUpdater]; - - /** - * Returns a function that will run the callback that was passed when - * calling this hook. Useful for accessing Recoil state in response to - * events. - */ - export function useRecoilCallback, Return>( - fn: (interface: CallbackInterface) => (...args: Args) => Return, - deps?: ReadonlyArray, - ): (...args: Args) => Return; - - /** - * Returns a function that executes an atomic transaction for updating Recoil state. - */ - export function useRecoilTransaction_UNSTABLE>( - fn: (interface: TransactionInterface_UNSTABLE) => (...args: Args) => void, - deps?: ReadonlyArray, - ): (...args: Args) => void; - - export function useRecoilTransactionObserver_UNSTABLE( - callback: (opts: { - snapshot: Snapshot, - previousSnapshot: Snapshot, - }) => void, - ): void; - - /** - * Updates Recoil state to match the provided snapshot. - */ - export function useGotoRecoilSnapshot(): (snapshot: Snapshot) => void; - - /** - * Returns a snapshot of the current Recoil state and subscribes the component - * to re-render when any state is updated. - */ - export function useRecoilSnapshot(): Snapshot; - - // useRecoilRefresher.d.ts - /** - * Clears the cache for a selector causing it to be reevaluated. - */ - export function useRecoilRefresher_UNSTABLE(recoilValue: RecoilValue): () => void; - - // useRecoilBridgeAcrossReactRoots.d.ts - export const RecoilBridge: React.FC<{children: React.ReactNode}>; - /** - * Returns a component that acts like a but shares the same store - * as the current . - */ - export function useRecoilBridgeAcrossReactRoots_UNSTABLE(): typeof RecoilBridge; - - // useRecoilStoreID - declare const StoreID_OPAQUE: unique symbol; - export interface StoreID { - readonly [StoreID_OPAQUE]: true; - } - /** - * Returns an ID for the currently active state store of the host - */ - export function useRecoilStoreID(): StoreID; - - // loadable.d.ts - interface BaseLoadable { - getValue: () => T; - toPromise: () => Promise; - valueOrThrow: () => T; - errorOrThrow: () => any; - promiseOrThrow: () => Promise; - is: (other: Loadable) => boolean; - map: (map: (from: T) => Loadable | Promise | S) => Loadable; - } - - interface ValueLoadable extends BaseLoadable { - state: 'hasValue'; - contents: T; - valueMaybe: () => T; - errorMaybe: () => undefined; - promiseMaybe: () => undefined; - } - - interface LoadingLoadable extends BaseLoadable { - state: 'loading'; - contents: Promise; - valueMaybe: () => undefined; - errorMaybe: () => undefined; - promiseMaybe: () => Promise; - } - - interface ErrorLoadable extends BaseLoadable { - state: 'hasError'; - contents: any; - valueMaybe: () => undefined; - errorMaybe: () => any; - promiseMaybe: () => undefined; - } - - export type Loadable = - | ValueLoadable - | LoadingLoadable - | ErrorLoadable; - - // recoilValue.d.ts - declare class AbstractRecoilValue { - __tag: [T]; - __cTag: (t: T) => void; // for contravariance - - key: NodeKey; - constructor(newKey: NodeKey); - toJSON(): {key: string}; - } - - declare class AbstractRecoilValueReadonly { - __tag: [T]; - - key: NodeKey; - constructor(newKey: NodeKey); - toJSON(): {key: string}; - } - - export class RecoilState extends AbstractRecoilValue {} - export class RecoilValueReadOnly extends AbstractRecoilValueReadonly {} - export type RecoilValue = RecoilValueReadOnly | RecoilState; - - /** - * Returns true if the parameter is a Recoil atom or selector. - */ - export function isRecoilValue(val: unknown): val is RecoilValue; // eslint-disable-line @typescript-eslint/no-explicit-any - - /** Utilities */ - - // bigint not supported yet - type Primitive = undefined | null | boolean | number | symbol | string; - interface HasToJSON { toJSON(): SerializableParam; } - - export type SerializableParam = - | Primitive - | HasToJSON - | ReadonlyArray - | ReadonlySet - | ReadonlyMap - | Readonly<{[key: string]: SerializableParam}>; - -interface AtomFamilyOptionsWithoutDefault { - key: NodeKey; - dangerouslyAllowMutability?: boolean; - effects?: | ReadonlyArray> | ((param: P) => ReadonlyArray>); - effects_UNSTABLE?: | ReadonlyArray> | ((param: P) => ReadonlyArray>); - // cachePolicyForParams_UNSTABLE?: CachePolicyWithoutEviction; TODO: removing while we discuss long term API - } - interface AtomFamilyOptionsWithDefault extends AtomFamilyOptionsWithoutDefault { - default: - | RecoilValue - | Promise - | Loadable - | WrappedValue - | T - | ((param: P) => T | RecoilValue | Promise | Loadable | WrappedValue); - } - export type AtomFamilyOptions = - | AtomFamilyOptionsWithDefault - | AtomFamilyOptionsWithoutDefault; - - /** - * Returns a function which returns a memoized atom for each unique parameter value. - */ - export function atomFamily( - options: AtomFamilyOptions, - ): (param: P) => RecoilState; - - export interface ReadOnlySelectorFamilyOptions { - key: string; - get: (param: P) => (opts: { - get: GetRecoilValue, - getCallback: GetCallback, - }) => Promise | RecoilValue | Loadable | WrappedValue | T; - // cachePolicyForParams_UNSTABLE?: CachePolicyWithoutEviction; TODO: removing while we discuss long term API - cachePolicy_UNSTABLE?: CachePolicyWithoutEquality; // TODO: using the more restrictive CachePolicyWithoutEquality while we discuss long term API - dangerouslyAllowMutability?: boolean; - } - - export interface ReadWriteSelectorFamilyOptions { - key: string; - get: (param: P) => (opts: { - get: GetRecoilValue, - getCallback: GetCallback, - }) => Promise | Loadable | WrappedValue | RecoilValue | T; - set: ( - param: P, - ) => ( - opts: { set: SetRecoilState; get: GetRecoilValue; reset: ResetRecoilState }, - newValue: T | DefaultValue, - ) => void; - // cachePolicyForParams_UNSTABLE?: CachePolicyWithoutEviction; TODO: removing while we discuss long term API - cachePolicy_UNSTABLE?: CachePolicyWithoutEquality; // TODO: using the more restrictive CachePolicyWithoutEquality while we discuss long term API - dangerouslyAllowMutability?: boolean; - } - -/** - * Returns a function which returns a memoized atom for each unique parameter value. - */ -export function selectorFamily( -options: ReadWriteSelectorFamilyOptions, -): (param: P) => RecoilState; - -/** - * Returns a function which returns a memoized atom for each unique parameter value. - */ -export function selectorFamily( -options: ReadOnlySelectorFamilyOptions, -): (param: P) => RecoilValueReadOnly; - -/** - * Returns a selector that always has a constant value. - */ -export function constSelector(constant: T): RecoilValueReadOnly; - -/** - * Returns a selector which is always in the provided error state. - */ -export function errorSelector(message: string): RecoilValueReadOnly; - -/** - * Casts a selector to be a read-only selector - */ -export function readOnlySelector(atom: RecoilValue): RecoilValueReadOnly; - -/** - * Returns a selector that has the value of the provided atom or selector as a Loadable. - * This means you can use noWait() to avoid entering an error or suspense state in - * order to manually handle those cases. - */ -export function noWait(state: RecoilValue): RecoilValueReadOnly>; - - /* eslint-disable @typescript-eslint/no-explicit-any */ - - export type UnwrapRecoilValue = T extends RecoilValue ? R : never; - - export type UnwrapRecoilValues> | { [key: string]: RecoilValue }> = { - [P in keyof T]: UnwrapRecoilValue; - }; - export type UnwrapRecoilValueLoadables> | { [key: string]: RecoilValue }> = { - [P in keyof T]: Loadable>; - }; - - export function waitForNone> | [RecoilValue]>( - param: RecoilValues, - ): RecoilValueReadOnly>; - - export function waitForNone }>( - param: RecoilValues, - ): RecoilValueReadOnly>; - - export function waitForAny> | [RecoilValue]>( - param: RecoilValues, - ): RecoilValueReadOnly>; - - export function waitForAny }>( - param: RecoilValues, - ): RecoilValueReadOnly>; - - export function waitForAll> | [RecoilValue]>( - param: RecoilValues, - ): RecoilValueReadOnly>; - - export function waitForAll }>( - param: RecoilValues, - ): RecoilValueReadOnly>; - - export function waitForAllSettled> | [RecoilValue]>( - param: RecoilValues, - ): RecoilValueReadOnly>; - - export function waitForAllSettled }>( - param: RecoilValues, - ): RecoilValueReadOnly>; - - export type UnwrapLoadable = T extends Loadable ? R : T extends Promise ? P : T; - export type UnwrapLoadables = { - [P in keyof T]: UnwrapLoadable; - }; - - /* eslint-disable @typescript-eslint/no-unused-vars */ - export namespace RecoilLoadable { - /** - * Factory to make a Loadable object. If a Promise is provided the Loadable will - * be in a 'loading' state until the Promise is either resolved or rejected. - */ - function of(x: T | Promise | Loadable): Loadable; - /** - * Factory to make a Loadable object in an error state. - */ - function error(x: any): ErrorLoadable; - /** - * Factory to make a loading Loadable which never resolves. - */ - function loading(): LoadingLoadable; - /** - * Factory to make a Loadable which is resolved when all of the Loadables provided - * to it are resolved or any one has an error. The value is an array of the values - * of all of the provided Loadables. This is comparable to Promise.all() for Loadables. - * Similar to Promise.all(), inputs may be Loadables, Promises, or literal values. - */ - function all]>(inputs: Inputs): Loadable>; - function all(inputs: Inputs): Loadable>; - /** - * Returns true if the provided parameter is a Loadable type. - */ - function isLoadable(x: any): x is Loadable; - } - /* eslint-enable @typescript-eslint/no-unused-vars */ - - /* eslint-enable @typescript-eslint/no-explicit-any */ - - /** - * Factory to produce a Recoil snapshot object with all atoms in the default state. - */ - export function snapshot_UNSTABLE(initializeState?: (shapshot: MutableSnapshot) => void): Snapshot; +// Stub file for dtslint diff --git a/typescript/tests.ts b/typescript/recoil-test.ts similarity index 100% rename from typescript/tests.ts rename to typescript/recoil-test.ts diff --git a/typescript/recoil.d.ts b/typescript/recoil.d.ts new file mode 100644 index 000000000..0462e5e26 --- /dev/null +++ b/typescript/recoil.d.ts @@ -0,0 +1,585 @@ +// Minimum TypeScript Version: 3.9 + +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @emails oncall+recoil + */ + +/** + * This file is a manual translation of the flow types, which are the source of truth, so we should not introduce new terminology or behavior in this file. + */ + + export { }; + + import * as React from 'react'; + + // state.d.ts + type NodeKey = string; + + // node.d.ts + export class DefaultValue { + private __tag: 'DefaultValue'; + } + + // recoilRoot.d.ts + export type RecoilRootProps = { + initializeState?: (mutableSnapshot: MutableSnapshot) => void, + override?: true, + children: React.ReactNode, + } | { + override: false, + children: React.ReactNode, + }; + + /** + * Root component for managing Recoil state. Most Recoil hooks should be + * called from a component nested in a + */ + export const RecoilRoot: React.FC; + + // Snapshot.d.ts + declare const SnapshotID_OPAQUE: unique symbol; + export interface SnapshotID { + readonly [SnapshotID_OPAQUE]: true; + } + + interface ComponentInfo { + name: string; + } + + interface RecoilStateInfo { + loadable?: Loadable; + isActive: boolean; + isSet: boolean; + isModified: boolean; // TODO report modified selectors + type: 'atom' | 'selector'; + deps: Iterable>; + subscribers: { + nodes: Iterable>, + components: Iterable, + }; + } + + export class Snapshot { + getID(): SnapshotID; + getLoadable(recoilValue: RecoilValue): Loadable; + getPromise(recoilValue: RecoilValue): Promise; + getNodes_UNSTABLE(opts?: { isModified?: boolean, isInitialized?: boolean }): Iterable>; + getInfo_UNSTABLE(recoilValue: RecoilValue): RecoilStateInfo; + map(cb: (mutableSnapshot: MutableSnapshot) => void): Snapshot; + asyncMap(cb: (mutableSnapshot: MutableSnapshot) => Promise): Promise; + retain(): () => void; + isRetained(): boolean; + } + + export class MutableSnapshot extends Snapshot { + set: SetRecoilState; + reset: ResetRecoilState; + } + + declare const WrappedValue_OPAQUE: unique symbol; + export interface WrappedValue { + readonly [WrappedValue_OPAQUE]: true; + } + + // Effect is called the first time a node is used with a + export type AtomEffect = (param: { + node: RecoilState, + storeID: StoreID, + parentStoreID_UNSTABLE?: StoreID, + trigger: 'set' | 'get', + + // Call synchronously to initialize value or async to change it later + setSelf: (param: + | T + | DefaultValue + | Promise + | WrappedValue + | ((param: T | DefaultValue) => T | DefaultValue | WrappedValue), + ) => void, + resetSelf: () => void, + + // Subscribe callbacks to events. + // Atom effect observers are called before global transaction observers + onSet: ( + param: (newValue: T, oldValue: T | DefaultValue, isReset: boolean) => void, + ) => void, + + // Accessors to read other atoms/selectors + getPromise: (recoilValue: RecoilValue) => Promise, + getLoadable: (recoilValue: RecoilValue) => Loadable, + getInfo_UNSTABLE: (recoilValue: RecoilValue) => RecoilStateInfo, + }) => void | (() => void); + + // atom.d.ts + interface AtomOptionsWithoutDefault { + key: NodeKey; + effects?: ReadonlyArray>; + effects_UNSTABLE?: ReadonlyArray>; + dangerouslyAllowMutability?: boolean; + } + interface AtomOptionsWithDefault extends AtomOptionsWithoutDefault { + default: RecoilValue | Promise | Loadable | WrappedValue | T; + } + export type AtomOptions = AtomOptionsWithoutDefault | AtomOptionsWithDefault; + + /** + * Creates an atom, which represents a piece of writeable state + */ + export function atom(options: AtomOptions): RecoilState; + export namespace atom { + function value(value: T): WrappedValue; + } + + export type GetRecoilValue = (recoilVal: RecoilValue) => T; + export type SetterOrUpdater = (valOrUpdater: ((currVal: T) => T) | T) => void; + export type Resetter = () => void; + export interface TransactionInterface_UNSTABLE { + get(a: RecoilValue): T; + set(s: RecoilState, u: ((currVal: T) => T) | T): void; + reset(s: RecoilState): void; + } + export interface CallbackInterface { + set: (recoilVal: RecoilState, valOrUpdater: ((currVal: T) => T) | T) => void; + reset: (recoilVal: RecoilState) => void; // eslint-disable-line @typescript-eslint/no-explicit-any + refresh: (recoilValue: RecoilValue) => void; + snapshot: Snapshot; + gotoSnapshot: (snapshot: Snapshot) => void; + transact_UNSTABLE: (cb: (i: TransactionInterface_UNSTABLE) => void) => void; + } + + // selector.d.ts + export interface SelectorCallbackInterface extends CallbackInterface { + node: RecoilState; // TODO This isn't properly typed + } + export type GetCallback = , Return>( + fn: (interface: SelectorCallbackInterface) => (...args: Args) => Return, + ) => (...args: Args) => Return; + + export type SetRecoilState = ( + recoilVal: RecoilState, + newVal: T | DefaultValue | ((prevValue: T) => T | DefaultValue), + ) => void; + + export type ResetRecoilState = (recoilVal: RecoilState) => void; // eslint-disable-line @typescript-eslint/no-explicit-any + + // export type EqualityPolicy = 'reference' | 'value'; TODO: removing while we discuss long term API + + export type EvictionPolicy = 'lru' | 'keep-all' | 'most-recent'; + + // TODO: removing while we discuss long term API + // export type CachePolicy = + // | {eviction: 'lru', maxSize: number, equality?: EqualityPolicy} + // | {eviction: 'none', equality?: EqualityPolicy} + // | {eviction?: undefined, equality: EqualityPolicy}; + + // TODO: removing while we discuss long term API + // export interface CachePolicyWithoutEviction { + // equality: EqualityPolicy; + // } + + export type CachePolicyWithoutEquality = {eviction: 'lru', maxSize: number} | {eviction: 'keep-all'} | {eviction: 'most-recent'}; + + export interface ReadOnlySelectorOptions { + key: string; + get: (opts: { + get: GetRecoilValue, + getCallback: GetCallback, + }) => Promise | RecoilValue | Loadable | WrappedValue | T; + dangerouslyAllowMutability?: boolean; + cachePolicy_UNSTABLE?: CachePolicyWithoutEquality; // TODO: using the more restrictive CachePolicyWithoutEquality while we discuss long term API + } + + export interface ReadWriteSelectorOptions extends ReadOnlySelectorOptions { + set: ( + opts: { + set: SetRecoilState; + get: GetRecoilValue; + reset: ResetRecoilState; + }, + newValue: T | DefaultValue, + ) => void; + } + + /** + * Creates a selector which represents derived state. + */ + export function selector(options: ReadWriteSelectorOptions): RecoilState; + export function selector(options: ReadOnlySelectorOptions): RecoilValueReadOnly; + export namespace selector { + function value(value: T): WrappedValue; + } + + // hooks.d.ts + + /** + * Returns the value of an atom or selector (readonly or writeable) and + * subscribes the components to future updates of that state. + */ + export function useRecoilValue(recoilValue: RecoilValue): T; + + /** + * Returns a Loadable representing the status of the given Recoil state + * and subscribes the component to future updates of that state. Useful + * for working with async selectors. + */ + export function useRecoilValueLoadable(recoilValue: RecoilValue): Loadable; + + /** + * Returns a tuple where the first element is the value of the recoil state + * and the second is a setter to update that state. Subscribes component + * to updates of the given state. + */ + export function useRecoilState(recoilState: RecoilState): [T, SetterOrUpdater]; + + /** + * Returns a tuple where the first element is a Loadable and the second + * element is a setter function to update the given state. Subscribes + * component to updates of the given state. + */ + export function useRecoilStateLoadable(recoilState: RecoilState): [Loadable, SetterOrUpdater]; + + /** + * Returns a setter function for updating Recoil state. Does not subscribe + * the component to the given state. + */ + + export function useSetRecoilState(recoilState: RecoilState): SetterOrUpdater; + + /** + * Returns a function that will reset the given state to its default value. + */ + export function useResetRecoilState(recoilState: RecoilState): Resetter; // eslint-disable-line @typescript-eslint/no-explicit-any + + /** + * Returns current info about an atom + */ + export function useGetRecoilValueInfo_UNSTABLE(): (recoilValue: RecoilValue) => RecoilStateInfo; + +/** + * Experimental version of hooks for useTransition() support + */ + export function useRecoilValue_TRANSITION_SUPPORT_UNSTABLE(recoilValue: RecoilValue): T; + export function useRecoilValueLoadable_TRANSITION_SUPPORT_UNSTABLE(recoilValue: RecoilValue): Loadable; + export function useRecoilState_TRANSITION_SUPPORT_UNSTABLE(recoilState: RecoilState): [T, SetterOrUpdater]; + + /** + * Returns a function that will run the callback that was passed when + * calling this hook. Useful for accessing Recoil state in response to + * events. + */ + export function useRecoilCallback, Return>( + fn: (interface: CallbackInterface) => (...args: Args) => Return, + deps?: ReadonlyArray, + ): (...args: Args) => Return; + + /** + * Returns a function that executes an atomic transaction for updating Recoil state. + */ + export function useRecoilTransaction_UNSTABLE>( + fn: (interface: TransactionInterface_UNSTABLE) => (...args: Args) => void, + deps?: ReadonlyArray, + ): (...args: Args) => void; + + export function useRecoilTransactionObserver_UNSTABLE( + callback: (opts: { + snapshot: Snapshot, + previousSnapshot: Snapshot, + }) => void, + ): void; + + /** + * Updates Recoil state to match the provided snapshot. + */ + export function useGotoRecoilSnapshot(): (snapshot: Snapshot) => void; + + /** + * Returns a snapshot of the current Recoil state and subscribes the component + * to re-render when any state is updated. + */ + export function useRecoilSnapshot(): Snapshot; + + // useRecoilRefresher.d.ts + /** + * Clears the cache for a selector causing it to be reevaluated. + */ + export function useRecoilRefresher_UNSTABLE(recoilValue: RecoilValue): () => void; + + // useRecoilBridgeAcrossReactRoots.d.ts + export const RecoilBridge: React.FC<{children: React.ReactNode}>; + /** + * Returns a component that acts like a but shares the same store + * as the current . + */ + export function useRecoilBridgeAcrossReactRoots_UNSTABLE(): typeof RecoilBridge; + + // useRecoilStoreID + declare const StoreID_OPAQUE: unique symbol; + export interface StoreID { + readonly [StoreID_OPAQUE]: true; + } + /** + * Returns an ID for the currently active state store of the host + */ + export function useRecoilStoreID(): StoreID; + + // loadable.d.ts + interface BaseLoadable { + getValue: () => T; + toPromise: () => Promise; + valueOrThrow: () => T; + errorOrThrow: () => any; + promiseOrThrow: () => Promise; + is: (other: Loadable) => boolean; + map: (map: (from: T) => Loadable | Promise | S) => Loadable; + } + + interface ValueLoadable extends BaseLoadable { + state: 'hasValue'; + contents: T; + valueMaybe: () => T; + errorMaybe: () => undefined; + promiseMaybe: () => undefined; + } + + interface LoadingLoadable extends BaseLoadable { + state: 'loading'; + contents: Promise; + valueMaybe: () => undefined; + errorMaybe: () => undefined; + promiseMaybe: () => Promise; + } + + interface ErrorLoadable extends BaseLoadable { + state: 'hasError'; + contents: any; + valueMaybe: () => undefined; + errorMaybe: () => any; + promiseMaybe: () => undefined; + } + + export type Loadable = + | ValueLoadable + | LoadingLoadable + | ErrorLoadable; + + // recoilValue.d.ts + declare class AbstractRecoilValue { + __tag: [T]; + __cTag: (t: T) => void; // for contravariance + + key: NodeKey; + constructor(newKey: NodeKey); + toJSON(): {key: string}; + } + + declare class AbstractRecoilValueReadonly { + __tag: [T]; + + key: NodeKey; + constructor(newKey: NodeKey); + toJSON(): {key: string}; + } + + export class RecoilState extends AbstractRecoilValue {} + export class RecoilValueReadOnly extends AbstractRecoilValueReadonly {} + export type RecoilValue = RecoilValueReadOnly | RecoilState; + + /** + * Returns true if the parameter is a Recoil atom or selector. + */ + export function isRecoilValue(val: unknown): val is RecoilValue; // eslint-disable-line @typescript-eslint/no-explicit-any + + /** Utilities */ + + // bigint not supported yet + type Primitive = undefined | null | boolean | number | symbol | string; + interface HasToJSON { toJSON(): SerializableParam; } + + export type SerializableParam = + | Primitive + | HasToJSON + | ReadonlyArray + | ReadonlySet + | ReadonlyMap + | Readonly<{[key: string]: SerializableParam}>; + +interface AtomFamilyOptionsWithoutDefault { + key: NodeKey; + dangerouslyAllowMutability?: boolean; + effects?: | ReadonlyArray> | ((param: P) => ReadonlyArray>); + effects_UNSTABLE?: | ReadonlyArray> | ((param: P) => ReadonlyArray>); + // cachePolicyForParams_UNSTABLE?: CachePolicyWithoutEviction; TODO: removing while we discuss long term API + } + interface AtomFamilyOptionsWithDefault extends AtomFamilyOptionsWithoutDefault { + default: + | RecoilValue + | Promise + | Loadable + | WrappedValue + | T + | ((param: P) => T | RecoilValue | Promise | Loadable | WrappedValue); + } + export type AtomFamilyOptions = + | AtomFamilyOptionsWithDefault + | AtomFamilyOptionsWithoutDefault; + + /** + * Returns a function which returns a memoized atom for each unique parameter value. + */ + export function atomFamily( + options: AtomFamilyOptions, + ): (param: P) => RecoilState; + + export interface ReadOnlySelectorFamilyOptions { + key: string; + get: (param: P) => (opts: { + get: GetRecoilValue, + getCallback: GetCallback, + }) => Promise | RecoilValue | Loadable | WrappedValue | T; + // cachePolicyForParams_UNSTABLE?: CachePolicyWithoutEviction; TODO: removing while we discuss long term API + cachePolicy_UNSTABLE?: CachePolicyWithoutEquality; // TODO: using the more restrictive CachePolicyWithoutEquality while we discuss long term API + dangerouslyAllowMutability?: boolean; + } + + export interface ReadWriteSelectorFamilyOptions { + key: string; + get: (param: P) => (opts: { + get: GetRecoilValue, + getCallback: GetCallback, + }) => Promise | Loadable | WrappedValue | RecoilValue | T; + set: ( + param: P, + ) => ( + opts: { set: SetRecoilState; get: GetRecoilValue; reset: ResetRecoilState }, + newValue: T | DefaultValue, + ) => void; + // cachePolicyForParams_UNSTABLE?: CachePolicyWithoutEviction; TODO: removing while we discuss long term API + cachePolicy_UNSTABLE?: CachePolicyWithoutEquality; // TODO: using the more restrictive CachePolicyWithoutEquality while we discuss long term API + dangerouslyAllowMutability?: boolean; + } + +/** + * Returns a function which returns a memoized atom for each unique parameter value. + */ +export function selectorFamily( +options: ReadWriteSelectorFamilyOptions, +): (param: P) => RecoilState; + +/** + * Returns a function which returns a memoized atom for each unique parameter value. + */ +export function selectorFamily( +options: ReadOnlySelectorFamilyOptions, +): (param: P) => RecoilValueReadOnly; + +/** + * Returns a selector that always has a constant value. + */ +export function constSelector(constant: T): RecoilValueReadOnly; + +/** + * Returns a selector which is always in the provided error state. + */ +export function errorSelector(message: string): RecoilValueReadOnly; + +/** + * Casts a selector to be a read-only selector + */ +export function readOnlySelector(atom: RecoilValue): RecoilValueReadOnly; + +/** + * Returns a selector that has the value of the provided atom or selector as a Loadable. + * This means you can use noWait() to avoid entering an error or suspense state in + * order to manually handle those cases. + */ +export function noWait(state: RecoilValue): RecoilValueReadOnly>; + + /* eslint-disable @typescript-eslint/no-explicit-any */ + + export type UnwrapRecoilValue = T extends RecoilValue ? R : never; + + export type UnwrapRecoilValues> | { [key: string]: RecoilValue }> = { + [P in keyof T]: UnwrapRecoilValue; + }; + export type UnwrapRecoilValueLoadables> | { [key: string]: RecoilValue }> = { + [P in keyof T]: Loadable>; + }; + + export function waitForNone> | [RecoilValue]>( + param: RecoilValues, + ): RecoilValueReadOnly>; + + export function waitForNone }>( + param: RecoilValues, + ): RecoilValueReadOnly>; + + export function waitForAny> | [RecoilValue]>( + param: RecoilValues, + ): RecoilValueReadOnly>; + + export function waitForAny }>( + param: RecoilValues, + ): RecoilValueReadOnly>; + + export function waitForAll> | [RecoilValue]>( + param: RecoilValues, + ): RecoilValueReadOnly>; + + export function waitForAll }>( + param: RecoilValues, + ): RecoilValueReadOnly>; + + export function waitForAllSettled> | [RecoilValue]>( + param: RecoilValues, + ): RecoilValueReadOnly>; + + export function waitForAllSettled }>( + param: RecoilValues, + ): RecoilValueReadOnly>; + + export type UnwrapLoadable = T extends Loadable ? R : T extends Promise ? P : T; + export type UnwrapLoadables = { + [P in keyof T]: UnwrapLoadable; + }; + + /* eslint-disable @typescript-eslint/no-unused-vars */ + export namespace RecoilLoadable { + /** + * Factory to make a Loadable object. If a Promise is provided the Loadable will + * be in a 'loading' state until the Promise is either resolved or rejected. + */ + function of(x: T | Promise | Loadable): Loadable; + /** + * Factory to make a Loadable object in an error state. + */ + function error(x: any): ErrorLoadable; + /** + * Factory to make a loading Loadable which never resolves. + */ + function loading(): LoadingLoadable; + /** + * Factory to make a Loadable which is resolved when all of the Loadables provided + * to it are resolved or any one has an error. The value is an array of the values + * of all of the provided Loadables. This is comparable to Promise.all() for Loadables. + * Similar to Promise.all(), inputs may be Loadables, Promises, or literal values. + */ + function all]>(inputs: Inputs): Loadable>; + function all(inputs: Inputs): Loadable>; + /** + * Returns true if the provided parameter is a Loadable type. + */ + function isLoadable(x: any): x is Loadable; + } + /* eslint-enable @typescript-eslint/no-unused-vars */ + + /* eslint-enable @typescript-eslint/no-explicit-any */ + + /** + * Factory to produce a Recoil snapshot object with all atoms in the default state. + */ + export function snapshot_UNSTABLE(initializeState?: (shapshot: MutableSnapshot) => void): Snapshot; diff --git a/typescript/tsconfig.json b/typescript/tsconfig.json index e1908e6fc..247bd1ec8 100644 --- a/typescript/tsconfig.json +++ b/typescript/tsconfig.json @@ -10,11 +10,11 @@ "noEmit": true, "forceConsistentCasingInFileNames": true, "baseUrl": ".", - "paths": {"recoil": ["./index.d.ts"]} + "paths": {"recoil": ["./recoil.d.ts"]} }, "files": [ - "index.d.ts", - "tests.ts", + "recoil.d.ts", + "recoil-test.ts", "refine.d.ts", "refine-test.ts", "recoil-sync.d.ts",