diff --git a/jest.config.js b/jest.config.js index be5760f588..d8ef40e4f1 100644 --- a/jest.config.js +++ b/jest.config.js @@ -10,6 +10,9 @@ module.exports = { globals: { 'ts-jest': { tsconfig: 'tsconfig.test.json', + diagnostics: { + ignoreCodes: [6133], + }, }, }, } diff --git a/package-lock.json b/package-lock.json index 6e82d6ccc9..d7a6b3737c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4950,9 +4950,9 @@ } }, "esbuild": { - "version": "0.8.57", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.8.57.tgz", - "integrity": "sha512-j02SFrUwFTRUqiY0Kjplwjm1psuzO1d6AjaXKuOR9hrY0HuPsT6sV42B6myW34h1q4CRy+Y3g4RU/cGJeI/nNA==", + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.11.13.tgz", + "integrity": "sha512-d5coY4dd4rVWle0WzrR8+32ukKtZroVJ/wJzOwbBEmoSFB/H3QME0l+3IAN5Sf3LtuoUSivdv1/b5rD7OykXeg==", "dev": true }, "escalade": { diff --git a/package.json b/package.json index 2bb167e9c9..1cb3daa50c 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,8 @@ "name": "@reduxjs/toolkit", "version": "1.6.0-alpha.0", "description": "The official, opinionated, batteries-included toolset for efficient Redux development", + "author": "Mark Erikson ", + "license": "MIT", "repository": { "type": "git", "url": "git+https://github.com/reduxjs/redux-toolkit.git" @@ -23,35 +25,6 @@ "module": "dist/redux-toolkit.esm.js", "unpkg": "dist/redux-toolkit.umd.min.js", "types": "dist/index.d.ts", - "exports": { - ".": { - "types": "./dist/index.d.ts", - "module": "./dist/redux-toolkit.esm.js", - "default": "./dist/index.js" - }, - "./query": { - "types": "./query/index.d.ts", - "module": "./query/rtk-query.esm.js", - "default": "./query/index.js" - }, - "./query/react": { - "types": "./query/react.d.ts", - "module": "./query/react/rtk-query-react.esm.js", - "default": "./query/react/index.js" - } - }, - "typesVersions": { - "*": { - "query": [ - "./query/index.d.ts" - ], - "query/react": [ - "./query/react.d.ts" - ] - } - }, - "author": "Mark Erikson ", - "license": "MIT", "devDependencies": { "@microsoft/api-extractor": "^7.13.2", "@testing-library/react": "^11.2.6", @@ -72,7 +45,7 @@ "console-testing-library": "^0.3.1", "convert-source-map": "^1.7.0", "cross-fetch": "^3.1.4", - "esbuild": "0.8.57", + "esbuild": "^0.11.13", "eslint-config-react-app": "^5.0.1", "fs-extra": "^9.1.0", "invariant": "^2.2.4", @@ -95,8 +68,8 @@ "yargs": "^15.3.1" }, "scripts": { - "build-ci": "rimraf dist query && tsc && node scripts/cli.js", - "build": "rimraf dist query && tsc && node scripts/cli.js --skipExtraction", + "build-ci": "rimraf dist && tsc && node scripts/cli.js", + "build": "rimraf dist && tsc && node scripts/cli.js --local", "dev": "tsdx watch --format cjs,esm,system,umd", "format": "prettier --write \"src/**/*.{ts,tsx}\" \"**/*.md\"", "format:check": "prettier --list-different \"src/**/*.{ts,tsx}\" \"docs/*/**.md\"", @@ -119,18 +92,19 @@ "redux-thunk": "^2.3.0", "reselect": "^4.0.0" }, - "sideEffects": false, - "jest": { - "globals": { - "ts-jest": { - "diagnostics": { - "ignoreCodes": [ - 6133 - ] - } - } + "peerDependencies": { + "react": "^16.14.0 || ^17.0.0", + "react-redux": "^7.2.1" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true } }, + "sideEffects": false, "bugs": { "url": "https://github.com/reduxjs/redux-toolkit/issues" }, diff --git a/query/react/package.json b/query/react/package.json index f5bf198deb..d9dadc88fd 100644 --- a/query/react/package.json +++ b/query/react/package.json @@ -5,14 +5,7 @@ "main": "../../dist/query/react/index.js", "module": "../../dist/query/react/rtk-query-react.esm.js", "unpkg": "../../dist/query/react/rtk-query-react.umd.min.js", - "types": "../../dist/query/react/index.d.ts", "author": "Mark Erikson ", "license": "MIT", - "typesVersions": { - "<4.1": { - "index.d.ts": [ - "../../dist/query/react/indexTs40.d.ts" - ] - } - } -} \ No newline at end of file + "types": "../../dist/query/react/index.d.ts" +} diff --git a/scripts/build.ts b/scripts/build.ts index 449aeb9a47..ba815e5d50 100644 --- a/scripts/build.ts +++ b/scripts/build.ts @@ -5,14 +5,9 @@ import terser from 'terser' import rollup from 'rollup' import path from 'path' import fs from 'fs-extra' -import MagicString from 'magic-string' -import { appendInlineSourceMap, getLocation } from './sourcemap' import ts from 'typescript' import { RawSourceMap, SourceMapConsumer } from 'source-map' import merge from 'merge-source-map' -import { extractInlineSourcemap, removeInlineSourceMap } from './sourcemap' -import type { BuildOptions, EntryPointOptions } from './types' -import assert from 'assert' import { Extractor, ExtractorConfig, @@ -20,6 +15,10 @@ import { } from '@microsoft/api-extractor' import yargs from 'yargs/yargs' +import { extractInlineSourcemap, removeInlineSourceMap } from './sourcemap' +import type { BuildOptions, EntryPointOptions } from './types' +import { appendInlineSourceMap, getLocation } from './sourcemap' + const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) const { argv } = yargs(process.argv) @@ -50,7 +49,6 @@ const buildTargets: BuildOptions[] = [ minify: true, env: 'production', }, - // ESM, embedded `process`, ES5 syntax: typical Webpack dev { format: 'esm', @@ -66,7 +64,6 @@ const buildTargets: BuildOptions[] = [ minify: false, env: '', }, - // ESM, pre-compiled "dev", ES2017 syntax: browser development { format: 'esm', @@ -104,7 +101,6 @@ const entryPoints: EntryPointOptions[] = [ entryPoint: 'src/index.ts', extractionConfig: 'api-extractor.json', }, - // TODO The alternate entry point outputs are likely not importable this way. Need to sort that out. { prefix: 'rtk-query', folder: 'query', @@ -114,7 +110,7 @@ const entryPoints: EntryPointOptions[] = [ { prefix: 'rtk-query-react', folder: 'query/react', - entryPoint: 'src/query/react.ts', + entryPoint: 'src/query/react/index.ts', extractionConfig: 'api-extractor.query-react.json', }, ] @@ -149,8 +145,12 @@ async function bundle(options: BuildOptions & EntryPointOptions) { target: 'esnext', sourcemap: 'inline', bundle: true, - external: ['react', 'react-redux'], format: format === 'umd' ? 'esm' : format, + // Needed to prevent auto-replacing of process.env.NODE_ENV in all builds + platform: 'neutral', + // Needed to return to normal lookup behavior when platform: 'neutral' + mainFields: ['browser', 'module', 'main'], + conditions: ['browser'], define: env ? { 'process.env.NODE_ENV': JSON.stringify(env), @@ -209,7 +209,6 @@ async function bundle(options: BuildOptions & EntryPointOptions) { const mergedSourcemap = merge(sourcemap, result.sourceMapText) let code = result.outputText - // TODO Is this used at all? let mapping: RawSourceMap = mergedSourcemap if (minify) { @@ -236,39 +235,6 @@ async function bundle(options: BuildOptions & EntryPointOptions) { console.log('Build artifact:', chunk.path) await fs.writeFile(chunk.path, code) await fs.writeJSON(chunk.path + '.map', mapping) - const smc = await new SourceMapConsumer(mapping) - /* - const stubMap = { - '../src/configureStore.ts': [ - `"reducer" is a required argument, and must be a function or an object of functions that can be passed to combineReducers`, - ], - } - for (const [source, stubList] of Object.entries(stubMap)) { - for (const stub of stubList) { - const originContent = smc.sourceContentFor(source) - const originLocation = getLocation(originContent, stub) - const bundledPosition = getLocation(code, stub) - const recoverLocation = smc.originalPositionFor({ - line: bundledPosition.line, - column: bundledPosition.column, - }) - assert.deepStrictEqual( - source, - recoverLocation.source, - `sourceFile: expected ${source} but got ${recoverLocation.source}` - ) - assert( - Math.abs(originLocation.line - recoverLocation.line) <= 1, - `line: expected ${originLocation.line} but got ${recoverLocation.line}` - ) - assert( - Math.abs(originLocation.column - recoverLocation.column) <= 1, - `column: expected ${originLocation.column} but got ${recoverLocation.column}` - ) - } - - } - */ } } @@ -302,6 +268,7 @@ async function buildUMD(outputPath: string, prefix: string) { } } +// Generates an index file to handle importing CJS dev/prod async function writeEntry(folder: string, prefix: string) { await fs.writeFile( path.join('dist', folder, 'index.js'), @@ -320,7 +287,7 @@ interface BuildArgs { } async function main({ skipExtraction = false, local = false }: BuildArgs) { - //await fs.remove(outputDir) + // Dist folder will be removed by rimraf beforehand so TSC can generate typedefs await fs.ensureDir(outputDir) for (let entryPoint of entryPoints) { @@ -337,16 +304,22 @@ async function main({ skipExtraction = false, local = false }: BuildArgs) { ) await Promise.all(bundlePromises) await writeEntry(folder, prefix) + } - if (folder) { - const packageSource = path.join('src', folder, 'package.json') - const packageDest = path.join(outputPath, 'package.json') - } - - await sleep(500) // hack, waiting file to save + // Run UMD builds after everything else so we don't have to sleep after each set + for (let entryPoint of entryPoints) { + const { folder } = entryPoint + const outputPath = path.join('dist', folder) await buildUMD(outputPath, entryPoint.prefix) } + // We need one additional package.json file in dist to support + // versioned types for TS <4.1 + fs.copyFileSync( + 'src/query/react/versionedTypes/package.json', + 'dist/query/react/versionedTypes/package.json' + ) + if (!skipExtraction) { for (let entryPoint of entryPoints) { // Load and parse the api-extractor.json file @@ -377,9 +350,7 @@ async function main({ skipExtraction = false, local = false }: BuildArgs) { } } } - - - // addSubpath() } + const { skipExtraction, local } = argv main({ skipExtraction, local }) diff --git a/src/query/core/module.ts b/src/query/core/module.ts index 3dcd863126..de46846985 100644 --- a/src/query/core/module.ts +++ b/src/query/core/module.ts @@ -25,7 +25,6 @@ import { FullTagDescription, } from '../endpointDefinitions' import { CombinedState, QueryKeys, RootState } from './apiState' -import './buildSelectors' import { Api, Module } from '../apiTypes' import { onFocus, onFocusLost, onOnline, onOffline } from './setupListeners' import { buildSlice } from './buildSlice' diff --git a/src/query/package.json b/src/query/package.json deleted file mode 100644 index ba9d7746ca..0000000000 --- a/src/query/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "@reduxjs/toolkit-query", - "version": "1.0.0", - "description": "", - "main": "./index.js", - "module": "./rtk-query.esm.js", - "unpkg": "./rtk-query.umd.min.js", - "types": "./index.d.ts", - "author": "Mark Erikson ", - "license": "MTI" -} \ No newline at end of file diff --git a/src/query/react/index.ts b/src/query/react/index.ts index 85f6378bbb..103854e5e0 100644 --- a/src/query/react/index.ts +++ b/src/query/react/index.ts @@ -1,26 +1,22 @@ -import { reactHooksModuleName, ReactHooksBaseEndpoints } from './module' +import { coreModule, coreModuleName } from '../core/module' +import { buildCreateApi, CreateApi } from '../createApi' +import { reactHooksModule, reactHooksModuleName } from './module' -import { EndpointDefinitions } from '../endpointDefinitions' +import { MutationHooks, QueryHooks } from './buildHooks' +import { + EndpointDefinitions, + QueryDefinition, + MutationDefinition, + QueryArgFrom, +} from '../endpointDefinitions' import { BaseQueryFn } from '../baseQueryTypes' -import { TS41Hooks } from './ts41Types' + +import { QueryKeys } from '../core/apiState' +import { PrefetchOptions } from '../core/module' export * from '..' export { ApiProvider } from './ApiProvider' -export { createApi } from './reactHooksCommonExports' -export * from './reactHooksCommonExports' +const createApi = buildCreateApi(coreModule(), reactHooksModule()) -declare module '../apiTypes' { - export interface ApiModules< - // eslint-disable-next-line @typescript-eslint/no-unused-vars - BaseQuery extends BaseQueryFn, - Definitions extends EndpointDefinitions, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - ReducerPath extends string, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - TagTypes extends string - > { - [reactHooksModuleName]: ReactHooksBaseEndpoints & - TS41Hooks - } -} +export { createApi, reactHooksModule } diff --git a/src/query/react/module.ts b/src/query/react/module.ts index 81a384ac5a..11d7cd6413 100644 --- a/src/query/react/module.ts +++ b/src/query/react/module.ts @@ -12,6 +12,8 @@ import { capitalize } from '../utils' import { safeAssign } from '../tsHelpers' import { BaseQueryFn } from '../baseQueryTypes' +import { HooksWithUniqueNames } from './versionedTypes/index' + import { useDispatch as rrUseDispatch, useSelector as rrUseSelector, @@ -24,35 +26,45 @@ import { PrefetchOptions } from '../core/module' export const reactHooksModuleName = Symbol() export type ReactHooksModule = typeof reactHooksModuleName -export interface ReactHooksBaseEndpoints< - Definitions extends EndpointDefinitions -> { - /** - * Endpoints based on the input endpoints provided to `createApi`, containing `select`, `hooks` and `action matchers`. - */ - endpoints: { - [K in keyof Definitions]: Definitions[K] extends QueryDefinition< - any, - any, - any, - any, - any - > - ? QueryHooks - : Definitions[K] extends MutationDefinition - ? MutationHooks - : never +declare module '../apiTypes' { + export interface ApiModules< + // eslint-disable-next-line @typescript-eslint/no-unused-vars + BaseQuery extends BaseQueryFn, + Definitions extends EndpointDefinitions, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + ReducerPath extends string, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + TagTypes extends string + > { + [reactHooksModuleName]: { + /** + * Endpoints based on the input endpoints provided to `createApi`, containing `select`, `hooks` and `action matchers`. + */ + endpoints: { + [K in keyof Definitions]: Definitions[K] extends QueryDefinition< + any, + any, + any, + any, + any + > + ? QueryHooks + : Definitions[K] extends MutationDefinition + ? MutationHooks + : never + } + /** + * A hook that accepts a string endpoint name, and provides a callback that when called, pre-fetches the data for that endpoint. + */ + usePrefetch>( + endpointName: EndpointName, + options?: PrefetchOptions + ): ( + arg: QueryArgFrom, + options?: PrefetchOptions + ) => void + } & HooksWithUniqueNames } - /** - * A hook that accepts a string endpoint name, and provides a callback that when called, pre-fetches the data for that endpoint. - */ - usePrefetch>( - endpointName: EndpointName, - options?: PrefetchOptions - ): ( - arg: QueryArgFrom, - options?: PrefetchOptions - ) => void } // type RR = typeof import('react-redux') diff --git a/src/query/react/reactHooksCommonExports.ts b/src/query/react/reactHooksCommonExports.ts deleted file mode 100644 index a9942d9c51..0000000000 --- a/src/query/react/reactHooksCommonExports.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { coreModule, coreModuleName } from '../core/module' -import { buildCreateApi, CreateApi } from '../createApi' -import { - reactHooksModule, - reactHooksModuleName, - ReactHooksBaseEndpoints, -} from './module' - -import { MutationHooks, QueryHooks } from './buildHooks' -import { - EndpointDefinitions, - QueryDefinition, - MutationDefinition, - QueryArgFrom, -} from '../endpointDefinitions' -import { BaseQueryFn } from '../baseQueryTypes' - -import { QueryKeys } from '../core/apiState' -import { PrefetchOptions } from '../core/module' - -const createApi = buildCreateApi(coreModule(), reactHooksModule()) - -export { createApi, reactHooksModule } diff --git a/src/query/react/ts40Types.ts b/src/query/react/ts40Types.ts deleted file mode 100644 index b9c38d0679..0000000000 --- a/src/query/react/ts40Types.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { EndpointDefinitions } from '../endpointDefinitions' -export declare type TS41Hooks = unknown -export {} diff --git a/src/query/react/versionedTypes/index.ts b/src/query/react/versionedTypes/index.ts new file mode 100644 index 0000000000..279cdceafb --- /dev/null +++ b/src/query/react/versionedTypes/index.ts @@ -0,0 +1,8 @@ +// This file is a dummy. In actual dev, we re-export the hooks type +// here. But, when published, the package.json in this folder will +// point TS to either ts40Types.d.ts or ts41Types.d.ts, and bypass +// index.d.ts completely. +// Overall, this setup allows us to selectively override the one +// file that has any difference between 4.1 and earlier, without +// having to ship two completely duplicate copies of our typedefs. +export { HooksWithUniqueNames } from './ts41Types' diff --git a/src/query/react/versionedTypes/package.json b/src/query/react/versionedTypes/package.json new file mode 100644 index 0000000000..978648c7f7 --- /dev/null +++ b/src/query/react/versionedTypes/package.json @@ -0,0 +1,14 @@ +{ + "typesVersions": { + ">=4.1": { + "index": [ + "./ts41Types.d.ts" + ] + }, + "<4.1": { + "index": [ + "./ts40Types.d.ts" + ] + } + } +} diff --git a/src/query/react/versionedTypes/ts40Types.ts b/src/query/react/versionedTypes/ts40Types.ts new file mode 100644 index 0000000000..645c8c2e7f --- /dev/null +++ b/src/query/react/versionedTypes/ts40Types.ts @@ -0,0 +1,9 @@ +import { EndpointDefinitions } from '../../endpointDefinitions' + +// For TS 4.0 and earlier, disallow use of the per-endpoint +// hooks defined at the root of each API object, because we +// can't use the string literal types here. +export declare type HooksWithUniqueNames< + Definitions extends EndpointDefinitions +> = unknown +export {} diff --git a/src/query/react/ts41Types.ts b/src/query/react/versionedTypes/ts41Types.ts similarity index 77% rename from src/query/react/ts41Types.ts rename to src/query/react/versionedTypes/ts41Types.ts index abeac1cb14..2f74095755 100644 --- a/src/query/react/ts41Types.ts +++ b/src/query/react/versionedTypes/ts41Types.ts @@ -1,12 +1,14 @@ -import { UseMutation, UseLazyQuery, UseQuery } from './buildHooks' +import { UseMutation, UseLazyQuery, UseQuery } from './../buildHooks' import { DefinitionType, EndpointDefinitions, MutationDefinition, QueryDefinition, -} from '../endpointDefinitions' +} from '../../endpointDefinitions' -export type TS41Hooks< +// For TS 4.1 and later, we can use string literal types to define +// the exact names of each endpoint's exported hooks +export type HooksWithUniqueNames< Definitions extends EndpointDefinitions > = keyof Definitions extends infer Keys ? Keys extends string diff --git a/test/test.ts b/test/test.ts new file mode 100644 index 0000000000..af3f3e24e4 --- /dev/null +++ b/test/test.ts @@ -0,0 +1,18 @@ +import { createSlice } from '@reduxjs/toolkit' +import { fetchBaseQuery } from '@reduxjs/toolkit/query' +import { createApi } from '@reduxjs/toolkit/query/react' + +// Define a service using a base URL and expected endpoints +export const pokemonApi = createApi({ + reducerPath: 'pokemonApi', + baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }), + endpoints: (builder) => ({ + getPokemonByName: builder.query({ + query: (name: string) => `pokemon/${name}`, + }), + }), +}) + +// Export hooks for usage in functional components, which are +// auto-generated based on the defined endpoints +export const { useGetPokemonByNameQuery } = pokemonApi diff --git a/test/tsconfig.json b/test/tsconfig.json new file mode 100644 index 0000000000..c885d002c3 --- /dev/null +++ b/test/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "strict": true, + "noEmit": true, + "target": "es2018", + "jsx": "react", + "baseUrl": ".", + "skipLibCheck": true, + "paths": { + "@reduxjs/toolkit": ["../"], + "@reduxjs/toolkit/*": ["../*"], + "@internal/*": ["../src/query/*"] + } + } +}