diff --git a/README.md b/README.md index 38808d5..0b1729d 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,3 @@ -[![Version](https://vsmarketplacebadge.apphb.com/version/dsznajder.es7-react-js-snippets.svg)](https://vsmarketplacebadge.apphb.com/version-short/dsznajder.es7-react-js-snippets.svg) -[![Install](https://vsmarketplacebadge.apphb.com/installs/dsznajder.es7-react-js-snippets.svg)](https://vsmarketplacebadge.apphb.com/installs-short/dsznajder.es7-react-js-snippets.svg) -[![Downloads](https://vsmarketplacebadge.apphb.com/downloads/dsznajder.es7-react-js-snippets.svg)](https://vsmarketplacebadge.apphb.com/downloads-short/dsznajder.es7-react-js-snippets.svg) -[![Ratings](https://vsmarketplacebadge.apphb.com/rating-short/dsznajder.es7-react-js-snippets.svg)](https://vsmarketplacebadge.apphb.com/rating-short/dsznajder.es7-react-js-snippets.svg) - # VS Code ES7+ React/Redux/React-Native/JS snippets JavaScript and React/Redux snippets in ES7+ with Babel plugin features for [VS Code](https://code.visualstudio.com/) @@ -25,28 +20,21 @@ ext install dsznajder.es7-react-js-snippets ## Options -From version 4 extension provides options to customize the behavior of the snippets: - -| Option | Description | -| ---------------: | ---------------------------------------------------------------------------- | -| languageScopes | list of supported languages / files recognition | -| prettierEnabled | determines if snippets should be parsed with project prettier config | -| importReactOnTop | If disabled, snippets won't contain `import React` on top. React 17+ support | -| typescript | adds additional typescript snippets | - -# Sponsors +| Option | Description | +| ----------------------: | ------------------------------------------------------------------------------------------------------ | +| languageScopes | Comma-separated list of language scopes where snippets are available (default: `typescript,typescriptreact,javascript,javascriptreact`) | +| importReactOnTop | Adds `import React` at the top of component snippets. Enable for legacy projects pre-React 17 (default: `false`) | +| typescript | Adds TypeScript-specific component snippets (default: `true`) | +| typescriptPropsStatePrefix | Controls `type` vs `interface` for TypeScript Props/State (default: `type`) | -


-Manage pull requests and conduct code reviews in your IDE with full source-tree context. Comment on any line, not just the diffs. Use jump-to-definition, your favorite keybindings, and code intelligence with more of your workflow.
Learn More

- -
+**Note:** Changing settings requires a VS Code restart to take effect. ### Conquer of Completion It is possible to use this package in your vim/neovim text editor, to make this possible, make sure you have the `coc.nvim` previously configured, then add this command to your `init.vim` ```shell -Plug 'dsznajder/vscode-es7-javascript-react-snippets', { 'do': 'yarn install --frozen-lockfile && yarn compile' } +Plug 'r5n-labs/vscode-react-javascript-snippets', { 'do': 'yarn install --frozen-lockfile && yarn compile' } ``` Update your vim / neovim settings with `:source %` and then install the new package with `:PlugInstall` @@ -60,7 +48,7 @@ Note: This example uses `vim-plug` as a package manager, feel free to use some o For use with packer the syntax is a little different. Just add in your `init.vim` or `init.lua`: ```shell -use {'dsznajder/vscode-es7-javascript-react-snippets', +use {'r5n-labs/vscode-react-javascript-snippets', run = 'yarn install --frozen-lockfile && yarn compile' } ``` diff --git a/docs/Snippets.md b/docs/Snippets.md index 135e201..6198ae3 100644 --- a/docs/Snippets.md +++ b/docs/Snippets.md @@ -13,7 +13,24 @@ I.E. `tsrcc` ### React Hooks -- Hooks from [official docs](https://reactjs.org/docs/hooks-reference.html) are added with hook name as prefix. +| Prefix | Method | +| ------------------------------: | ------------------------------------------------------------------------- | +| `useStateSnippet→` | `const [state, setState] = useState(initialValue)` | +| `useEffectSnippet→` | `useEffect` with cleanup function and dependency array | +| `useContextSnippet→` | `const value = useContext(MyContext)` | +| `useReducerSnippet→` | `const [state, dispatch] = useReducer(reducer, initial, init)` | +| `useCallbackSnippet→` | `useCallback` with dependency array | +| `useMemoSnippet→` | `useMemo` with dependency array | +| `useRefSnippet→` | `const ref = useRef(initialValue)` | +| `useImperativeHandleSnippet→` | `useImperativeHandle` with ref and factory | +| `useLayoutEffectSnippet→` | `useLayoutEffect` with cleanup and dependency array | +| `useIdSnippet→` | `const id = useId()` | +| `useTransitionSnippet→` | `const [isPending, startTransition] = useTransition()` | +| `useDeferredValueSnippet→` | `const deferred = useDeferredValue(value)` | +| `useSnippet→` | `const value = use(resource)` (Promises or Context) | +| `useActionStateSnippet→` | `useActionState` with async handler, returns `[state, action, isPending]` | +| `useFormStatusSnippet→` | `const { pending, data, method, action } = useFormStatus()` (from `react-dom`) | +| `useOptimisticSnippet→` | `useOptimistic` with state and updater function | ### Basic Methods @@ -41,6 +58,9 @@ I.E. `tsrcc` | `sti→` | `setInterval(() => { }, intervalTime` | | `sto→` | `setTimeout(() => { }, delayTime` | | `prom→` | `return new Promise((resolve, reject) => { }` | +| `pge→` | `get propertyName() { }` | +| `pse→` | `set propertyName(value) { }` | +| `tpf→` | `typeof operand` | | `cmmb→` | `comment block` | | `cp→` | `const { } = this.props` | | `cs→` | `const { } = this.state` | @@ -51,19 +71,27 @@ I.E. `tsrcc` | ----------: | --------------------------------------------------------------------------- | | `imr→` | `import React from 'react'` | | `imrd→` | `import ReactDOM from 'react-dom'` | -| `imrc→` | `import React, { Component } from 'react'` | -| `imrpc→` | `import React, { PureComponent } from 'react'` | -| `imrm→` | `import React, { memo } from 'react'` | +| `imrc→` | `import { Component } from 'react'` | +| `imrcp→` | `import { Component } from 'react'` + `import PropTypes from 'prop-types'` | +| `imrpc→` | `import { PureComponent } from 'react'` | +| `imrpcp→` | `import { PureComponent } from 'react'` + `import PropTypes from 'prop-types'` | +| `imrm→` | `import { memo } from 'react'` | +| `imrmp→` | `import { memo } from 'react'` + `import PropTypes from 'prop-types'` | +| `impt→` | `import PropTypes from 'prop-types'` | | `imrr→` | `import { BrowserRouter as Router, Route, NavLink} from 'react-router-dom'` | | `imbr→` | `import { BrowserRouter as Router} from 'react-router-dom'` | -| `imbrc→` | `import { Route, Switch, NavLink, Link } from react-router-dom'` | -| `imbrr→` | `import { Route } from 'react-router-dom'` | -| `imbrs→` | `import { Switch } from 'react-router-dom'` | +| `imbrc→` | `import { Routes, Route, NavLink, Link } from 'react-router-dom'` | | `imbrl→` | `import { Link } from 'react-router-dom'` | | `imbrnl→` | `import { NavLink } from 'react-router-dom'` | -| `imrs→` | `import React, { useState } from 'react'` | -| `imrse→` | `import React, { useState, useEffect } from 'react'` | +| `imrrs→` | `import { Routes, Route } from 'react-router-dom'` | +| `imcbr→` | `import { createBrowserRouter, RouterProvider } from 'react-router-dom'` | +| `imnav→` | `import { useNavigate } from 'react-router-dom'` | +| `impar→` | `import { useParams } from 'react-router-dom'` | +| `imsp→` | `import { useSearchParams } from 'react-router-dom'` | +| `imld→` | `import { useLoaderData } from 'react-router-dom'` | +| `imfet→` | `import { useFetcher } from 'react-router-dom'` | | `redux→` | `import { connect } from 'react-redux'` | +| `rconst→` | `constructor(props) { }` with state initialization | | `est→` | `this.state = { }` | | `cdm→` | `componentDidMount = () => { }` | | `scu→` | `shouldComponentUpdate = (nextProps, nextState) => { }` | @@ -77,15 +105,23 @@ I.E. `tsrcc` | `state→` | `this.state.stateName` | | `rcontext→` | `const $1 = React.createContext()` | | `cref→` | `this.$1Ref = React.createRef()` | -| `fref→` | `const ref = React.createRef()` | | `bnd→` | `this.methodName = this.methodName.bind(this)` | ### React Native -| Prefix | Method | -| ---------: | -------------------------------------- | -| `imrn→` | `import { $1 } from 'react-native'` | -| `rnstyle→` | `const styles = StyleSheet.create({})` | +| Prefix | Method | +| ---------: | ------------------------------------------------ | +| `imrn→` | `import { $1 } from 'react-native'` | +| `rnstyle→` | `const styles = StyleSheet.create({})` | +| `rnc→` | React Native class component | +| `rncs→` | React Native class component with StyleSheet | +| `rnce→` | React Native class component with named export | +| `rnpc→` | React Native PureComponent | +| `rnpce→` | React Native PureComponent with named export | +| `rnf→` | React Native functional component | +| `rnfe→` | React Native functional component with named export | +| `rnfs→` | React Native functional component with StyleSheet | +| `rnfes→` | React Native functional component with StyleSheet and named export | ### Redux @@ -96,6 +132,9 @@ I.E. `tsrcc` | `rxreducer→` | `redux reducer template` | | `rxselect→` | `redux selector template` | | `rxslice→` | `redux slice template` | +| `rxslicex→` | `redux slice with extraReducers (pending/fulfilled/rejected)` | +| `rxthunk→` | `redux createAsyncThunk template` | +| `rxapi→` | `RTK Query createApi with fetchBaseQuery` | ### PropTypes @@ -129,8 +168,9 @@ I.E. `tsrcc` | `ptoor→` | `PropTypes.objectOf(name).isRequired` | | `ptsh→` | `PropTypes.shape({ })` | | `ptshr→` | `PropTypes.shape({ }).isRequired` | +| `ptex→` | `PropTypes.exact({ })` | +| `ptexr→` | `PropTypes.exact({ }).isRequired` | | `ptany→` | `PropTypes.any` | -| `ptypes→` | `static propTypes = {}` | ### Console @@ -151,17 +191,34 @@ I.E. `tsrcc` | `ctr→` | `console.trace(object)` | | `cwa→` | `console.warn` | | `cin→` | `console.info` | +| `ctl→` | `console.table` | + +### React 19 Directives + +| Prefix | Method | +| -----: | -------------------------------- | +| `usc` | `'use client'` directive | +| `uss` | `'use server'` directive | + +### React Router v6 Setup + +| Prefix | Method | +| ----------: | ----------------------------------------------------------------- | +| `rtrsetup→` | Full `createBrowserRouter` setup with `RouterProvider` and routes | +| `rtrla→` | Route module with `loader`, `action`, and `useLoaderData` | ### React Components +> **Note:** Examples below show output with `importReactOnTop` set to `false` (default). When enabled, component snippets will include `import React from 'react'` at the top. + ### `rcc` ```javascript -import React, { Component } from 'react' +import { Component } from 'react' export default class FileName extends Component { render() { - return
$2
+ return <>$2 } } ``` @@ -169,11 +226,11 @@ export default class FileName extends Component { ### `rce` ```javascript -import React, { Component } from 'react' +import { Component } from 'react' export class FileName extends Component { render() { - return
$2
+ return <>$2 } } @@ -183,14 +240,14 @@ export default $1 ### `rcep` ```javascript -import React, { Component } from 'react' +import { Component } from 'react' import PropTypes from 'prop-types' export class FileName extends Component { static propTypes = {} render() { - return
$2
+ return <>$2 } } @@ -200,11 +257,11 @@ export default $1 ### `rpc` ```javascript -import React, { PureComponent } from 'react' +import { PureComponent } from 'react' export default class FileName extends PureComponent { render() { - return
$2
+ return <>$2 } } ``` @@ -212,14 +269,14 @@ export default class FileName extends PureComponent { ### `rpcp` ```javascript -import React, { PureComponent } from 'react' +import { PureComponent } from 'react' import PropTypes from 'prop-types' export default class FileName extends PureComponent { static propTypes = {} render() { - return
$2
+ return <>$2 } } ``` @@ -227,14 +284,14 @@ export default class FileName extends PureComponent { ### `rpce` ```javascript -import React, { PureComponent } from 'react' +import { PureComponent } from 'react' import PropTypes from 'prop-types' export class FileName extends PureComponent { static propTypes = {} render() { - return
$2
+ return <>$2 } } @@ -244,7 +301,7 @@ export default FileName ### `rccp` ```javascript -import React, { Component } from 'react' +import { Component } from 'react' import PropTypes from 'prop-types' export default class FileName extends Component { @@ -253,7 +310,7 @@ export default class FileName extends Component { } render() { - return
$4
+ return <>$4 } } ``` @@ -261,11 +318,10 @@ export default class FileName extends Component { ### `rfcp` ```javascript -import React from 'react' import PropTypes from 'prop-types' function $1(props) { - return
$0
+ return <>$0 } $1.propTypes = {} @@ -276,20 +332,16 @@ export default $1 ### `rfc` ```javascript -import React from 'react' - export default function $1() { - return
$0
+ return <>$0 } ``` ### `rfce` ```javascript -import React from 'react' - function $1() { - return
$0
+ return <>$0 } export default $1 @@ -298,11 +350,10 @@ export default $1 ### `rafcp` ```javascript -import React from 'react' import PropTypes from 'prop-types' const $1 = (props) => { - return
$0
+ return <>$0 } $1.propTypes = {} @@ -313,20 +364,16 @@ export default $1 ### `rafc` ```javascript -import React from 'react' - export const $1 = () => { - return
$0
+ return <>$0 } ``` ### `rafce` ```javascript -import React from 'react' - const $1 = () => { - return
$0
+ return <>$0 } export default $1 @@ -335,21 +382,21 @@ export default $1 ### `rmc` ```javascript -import React, { memo } from 'react' +import { memo } from 'react' export default memo(function $1() { - return
$0
+ return <>$0 }) ``` ### `rmcp` ```javascript -import React, { memo } from 'react' +import { memo } from 'react' import PropTypes from 'prop-types' const $1 = memo(function $1(props) { - return
$0
+ return <>$0 }) $1.propTypes = {} @@ -360,12 +407,12 @@ export default $1 ### `rcredux` ```javascript -import React, { Component } from 'react' +import { Component } from 'react' import { connect } from 'react-redux' export class FileName extends Component { render() { - return
$4
+ return <>$4 } } @@ -379,7 +426,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(FileName) ### `rcreduxp` ```javascript -import React, { Component } from 'react' +import { Component } from 'react' import PropTypes from 'prop-types' import { connect } from 'react-redux' @@ -389,7 +436,7 @@ export class FileName extends Component { } render() { - return
$4
+ return <>$4 } } @@ -403,11 +450,10 @@ export default connect(mapStateToProps, mapDispatchToProps)(FileName) ### `rfcredux` ```javascript -import React, { Component } from 'react' import { connect } from 'react-redux' export const FileName = () => { - return
$4
+ return <>$4 } const mapStateToProps = (state) => ({}) @@ -417,42 +463,40 @@ const mapDispatchToProps = {} export default connect(mapStateToProps, mapDispatchToProps)(FileName) ``` -### `rfreduxp` +### `reduxmap` ```javascript -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { connect } from 'react-redux' - -export const FileName = () => { - return
$4
-} - -FileName.propTypes = { - $2: $3, -} - const mapStateToProps = (state) => ({}) const mapDispatchToProps = {} - -export default connect(mapStateToProps, mapDispatchToProps)(FileName) ``` -### `reduxmap` +## TypeScript Components -```javascript -const mapStateToProps = (state) => ({}) +All TypeScript component snippets use `type` for Props/State by default. Change to `interface` via the `typescriptPropsStatePrefix` setting. -const mapDispatchToProps = {} -``` +| Prefix | Method | +| ----------: | ------------------------------------------------------------ | +| `exptp→` | `export type` definition | +| `expint→` | `export interface` definition | +| `tsrcc→` | TypeScript class component with Props/State | +| `tsrce→` | TypeScript class component with named export | +| `tsrfce→` | TypeScript functional component with named export | +| `tsrfc→` | TypeScript functional component with default export | +| `tsrafce→` | TypeScript arrow function component with named export | +| `tsrafc→` | TypeScript arrow function component | +| `tsrpc→` | TypeScript PureComponent | +| `tsrpce→` | TypeScript PureComponent with named export | +| `tsrcredux→`| TypeScript class component with Redux | +| `tsrnf→` | TypeScript React Native arrow function component | +| `tsrnfs→` | TypeScript React Native arrow function with StyleSheet | ## React Native Components ### `rnc` ```javascript -import React, { Component } from 'react' +import { Component } from 'react' import { Text, View } from 'react-native' export default class FileName extends Component { @@ -469,7 +513,6 @@ export default class FileName extends Component { ### `rnf` ```javascript -import React from 'react' import { View, Text } from 'react-native' export default function $1() { @@ -484,7 +527,6 @@ export default function $1() { ### `rnfs` ```javascript -import React from 'react' import { StyleSheet, View, Text } from 'react-native' export default function $1() { @@ -501,7 +543,6 @@ const styles = StyleSheet.create({}) ### `rnfe` ```javascript -import React from 'react' import { View, Text } from 'react-native' const $1 = () => { @@ -518,7 +559,6 @@ export default $1 ### `rnfes` ```javascript -import React from 'react' import { StyleSheet, View, Text } from 'react-native' const $1 = () => { @@ -537,7 +577,7 @@ const styles = StyleSheet.create({}) ### `rncs` ```javascript -import React, { Component } from 'react' +import { Component } from 'react' import { Text, StyleSheet, View } from 'react-native' export default class FileName extends Component { @@ -556,7 +596,7 @@ const styles = StyleSheet.create({}) ### `rnce` ```javascript -import React, { Component } from 'react' +import { Component } from 'react' import { Text, View } from 'react-native' export class FileName extends Component { @@ -608,10 +648,25 @@ it('should $1', () => { }) ``` +### `tita` + +```javascript +it('should $1', async () => { + $2 +}) +``` + +### `testa` + +```javascript +test('should $1', async () => { + $2 +}) +``` + ### `stest` ```javascript -import React from 'react' import renderer from 'react-test-renderer' import { $1 } from '../$1' @@ -629,7 +684,6 @@ describe('<$1 />', () => { ### `srtest` ```javascript -import React from 'react' import renderer from 'react-test-renderer' import { Provider } from 'react-redux' @@ -654,7 +708,6 @@ describe('<$1 />', () => { ```javascript import 'react-native' -import React from 'react' import renderer from 'react-test-renderer' import $1 from '../$1' @@ -674,7 +727,6 @@ describe('<$1 />', () => { ```javascript import 'react-native' -import React from 'react' import renderer from 'react-test-renderer' import { Provider } from 'react-redux' @@ -698,7 +750,6 @@ describe('<$1 />', () => { ### `hocredux` ```javascript -import React from 'react' import PropTypes from 'prop-types' import { connect } from 'react-redux' @@ -721,7 +772,6 @@ export default (WrapperComponent) => ### `hoc` ```javascript -import React from 'react' import PropTypes from 'prop-types' export default (WrappedComponent) => { diff --git a/package.json b/package.json index 12df135..912f2b3 100644 --- a/package.json +++ b/package.json @@ -40,9 +40,9 @@ }, "activationEvents": [ "onLanguage:typescript", - "onLanguage:typescriptReact", + "onLanguage:typescriptreact", "onLanguage:javascript", - "onLanguage:javascriptReact", + "onLanguage:javascriptreact", "onCommand:reactSnippets.search", "onStartupFinished" ], @@ -64,15 +64,10 @@ "configuration": { "title": "ES React/React-Native/Redux snippets", "properties": { - "reactSnippets.settings.prettierEnabled": { - "type": "boolean", - "markdownDescription": "[EXPERIMENTAL: MIGHT NOT WORK]: Integrate prettier settings with code generated from snippets.", - "default": false - }, "reactSnippets.settings.importReactOnTop": { "type": "boolean", - "markdownDescription": "Controls if snippets should add `import React from 'react';` at the top of components.\nUse if you have React +17 and use jsx transform.", - "default": true + "markdownDescription": "Controls if snippets should add `import React from 'react';` at the top of components.\nEnable for legacy projects (pre-React 17).", + "default": false }, "reactSnippets.settings.typescript": { "type": "boolean", @@ -122,18 +117,11 @@ "watch": "tsc -watch -p ./", "typescript": "tsc --noEmit" }, - "dependencies": { - "prettier": "2.5.1" - }, - "peerDependencies": { - "prettier": "^2" - }, "devDependencies": { "@babel/cli": "7.17.0", "@babel/eslint-parser": "7.17.0", "@babel/preset-typescript": "7.16.7", "@types/node": "17.0.16", - "@types/prettier": "2.4.3", "@types/vscode": "^1.60.0", "@typescript-eslint/eslint-plugin": "5.11.0", "@typescript-eslint/parser": "5.11.0", diff --git a/src/helpers/extensionConfig.ts b/src/helpers/extensionConfig.ts index 5301a46..38f7621 100644 --- a/src/helpers/extensionConfig.ts +++ b/src/helpers/extensionConfig.ts @@ -2,15 +2,20 @@ import { workspace } from 'vscode'; export type ExtensionSettings = { languageScopes: string; - prettierEnabled: boolean; importReactOnTop: boolean; typescript: boolean; typescriptPropsStatePrefix: 'type' | 'interface'; }; -const extensionConfig = () => - workspace.getConfiguration( - 'reactSnippets.settings', - ) as unknown as ExtensionSettings; +const extensionConfig = (): ExtensionSettings => { + const config = workspace.getConfiguration('reactSnippets.settings'); + + return { + languageScopes: config.get('languageScopes', 'typescript,typescriptreact,javascript,javascriptreact'), + importReactOnTop: config.get('importReactOnTop', false), + typescript: config.get('typescript', true), + typescriptPropsStatePrefix: config.get<'type' | 'interface'>('typescriptPropsStatePrefix', 'type'), + }; +}; export default extensionConfig; diff --git a/src/helpers/formatters.ts b/src/helpers/formatters.ts index 6188083..83c220b 100644 --- a/src/helpers/formatters.ts +++ b/src/helpers/formatters.ts @@ -1,22 +1,10 @@ -import prettier from 'prettier'; - -import extensionConfig from './extensionConfig'; -import getPrettierConfig from './getPrettierConfig'; import { replaceSnippetPlaceholders, revertSnippetPlaceholders, } from './snippetPlaceholders'; -export const formatSnippet = (snippetString: string) => { - return extensionConfig().prettierEnabled - ? prettier.format(snippetString, getPrettierConfig()) - : snippetString; -}; - export const parseSnippet = (body: string | string[]) => { const snippetBody = typeof body === 'string' ? body : body.join('\n'); - return replaceSnippetPlaceholders( - formatSnippet(revertSnippetPlaceholders(snippetBody)), - ); + return replaceSnippetPlaceholders(revertSnippetPlaceholders(snippetBody)); }; diff --git a/src/helpers/generateSnippets.ts b/src/helpers/generateSnippets.ts index 579dbde..42f8339 100644 --- a/src/helpers/generateSnippets.ts +++ b/src/helpers/generateSnippets.ts @@ -1,4 +1,5 @@ -import { writeFile } from 'fs'; +import { writeFile } from 'fs/promises'; +import path from 'path'; import componentsSnippets, { ComponentsSnippet, @@ -19,10 +20,33 @@ import typescriptSnippets, { TypescriptSnippet, } from '../sourceSnippets/typescript'; +import { window } from 'vscode'; + import extensionConfig from './extensionConfig'; import parseSnippetToBody from './parseSnippetToBody'; import { replaceSnippetPlaceholders } from './snippetPlaceholders'; +const VALID_LANGUAGE_SCOPES = [ + 'typescript', + 'typescriptreact', + 'javascript', + 'javascriptreact', +]; + +const validateLanguageScopes = (scopes: string) => { + const requested = scopes.split(',').map((s) => s.trim()).filter(Boolean); + const valid = requested.filter((s) => VALID_LANGUAGE_SCOPES.includes(s)); + const invalid = requested.filter((s) => !VALID_LANGUAGE_SCOPES.includes(s)); + + if (invalid.length > 0) { + window.showWarningMessage( + `React Snippets: Invalid language scopes ignored: ${invalid.join(', ')}. Valid values: ${VALID_LANGUAGE_SCOPES.join(', ')}`, + ); + } + + return valid.length > 0 ? valid.join(',') : VALID_LANGUAGE_SCOPES.join(','); +}; + export type SnippetKeys = | OthersSnippet['key'] | HooksSnippet['key'] @@ -52,7 +76,8 @@ export type Snippets = { }; const getSnippets = () => { - const { typescript, languageScopes } = extensionConfig(); + const { typescript, languageScopes: rawScopes } = extensionConfig(); + const languageScopes = validateLanguageScopes(rawScopes); const snippets = [ ...(typescript ? typescriptSnippets : []), @@ -66,7 +91,7 @@ const getSnippets = () => { ...testsSnippets, ...othersSnippets, ].reduce((acc, snippet) => { - acc[snippet.key] = Object.assign(snippet, { + acc[snippet.key] = Object.assign({}, snippet, { body: parseSnippetToBody(snippet), scope: languageScopes, }); @@ -76,19 +101,12 @@ const getSnippets = () => { return replaceSnippetPlaceholders(JSON.stringify(snippets, null, 2)); }; -const generateSnippets = () => - new Promise((resolve) => { - const jsonSnippets = getSnippets(); - writeFile( - __dirname + '/../snippets/generated.json', - jsonSnippets, - (error) => { - if (error) { - console.error(error); - } - return resolve(true); - }, - ); - }); +const generateSnippets = async () => { + const jsonSnippets = getSnippets(); + await writeFile( + path.join(__dirname, '..', 'snippets', 'generated.json'), + jsonSnippets, + ); +}; export default generateSnippets; diff --git a/src/helpers/getPrettierConfig.ts b/src/helpers/getPrettierConfig.ts deleted file mode 100644 index 30fc1bd..0000000 --- a/src/helpers/getPrettierConfig.ts +++ /dev/null @@ -1,19 +0,0 @@ -import prettier, { Options } from 'prettier'; - -import extensionConfig from './extensionConfig'; - -let prettierConfig: prettier.Options | null; -prettier - .resolveConfig('', { editorconfig: true }) - .then((config) => (prettierConfig = config)); - -const getPrettierConfig = (): Options => { - const { prettierEnabled } = extensionConfig(); - - return { - parser: 'typescript', - ...(prettierEnabled && prettierConfig), - }; -}; - -export default getPrettierConfig; diff --git a/src/helpers/parseSnippetToBody.ts b/src/helpers/parseSnippetToBody.ts index b8a7e7e..22a4774 100644 --- a/src/helpers/parseSnippetToBody.ts +++ b/src/helpers/parseSnippetToBody.ts @@ -1,5 +1,4 @@ import extensionConfig from './extensionConfig'; -import { formatSnippet } from './formatters'; import { Snippet } from './generateSnippets'; import replaceOrRemoveReactImport from './replaceOrRemoveReactImport'; @@ -10,14 +9,9 @@ const parseSnippetToBody = (snippet: Snippet) => { const snippetBody = importReactOnTop ? body - : replaceOrRemoveReactImport({ - prefix: snippet.prefix, - body: snippet.body, - }); + : replaceOrRemoveReactImport(snippet.body); - const formattedSnippet = formatSnippet(snippetBody).split('\n'); - - return formattedSnippet; + return snippetBody.split('\n'); }; export default parseSnippetToBody; diff --git a/src/helpers/replaceOrRemoveReactImport.ts b/src/helpers/replaceOrRemoveReactImport.ts index 74d4e51..ac7b556 100644 --- a/src/helpers/replaceOrRemoveReactImport.ts +++ b/src/helpers/replaceOrRemoveReactImport.ts @@ -1,46 +1,4 @@ -import { Snippet } from './generateSnippets'; - -const snippetWithReactImportPrefixes = [ - 'rfce', - 'rfc', - 'rfcp', - 'rafce', - 'rafc', - 'rafcp', - 'rnfe', - 'rnfes', - 'rnf', - 'rnfs', - 'stest', - 'sntest', - 'srtest', - 'snrtest', - 'hocredux', - 'hoc', - 'tsrafc', - 'tsrafce', - 'tsrcc', - 'tsrcredux', - 'tsrce', - 'tsrpce', - 'tsrpc', - 'tsrfc', - 'tsrfce', - 'tsrnf', - 'tsrnfs', -]; - -const replaceOrRemoveReactImport = ({ - body, - prefix, -}: { - body: string[]; - prefix: Snippet['prefix']; -}) => { - if (!snippetWithReactImportPrefixes.includes(prefix)) { - return body.join('\n'); - } - +const replaceOrRemoveReactImport = (body: string[]) => { let bodyCopy = [...body]; const reactImportIndex = bodyCopy.findIndex((line) => line.match(new RegExp(/import React/, 'g')), diff --git a/src/helpers/snippetSearch.ts b/src/helpers/snippetSearch.ts index bb2855a..ec88f20 100644 --- a/src/helpers/snippetSearch.ts +++ b/src/helpers/snippetSearch.ts @@ -1,5 +1,6 @@ -import { readFileSync } from 'fs'; -import { SnippetString, window } from 'vscode'; +import { readFile } from 'fs/promises'; +import path from 'path'; +import { commands, SnippetString, window } from 'vscode'; import { parseSnippet } from './formatters'; import { Snippet } from './generateSnippets'; @@ -7,15 +8,19 @@ import { Snippet } from './generateSnippets'; const snippetSearch = async () => { const { showQuickPick, activeTextEditor } = window; - const snippets = readFileSync( - __dirname + '/../snippets/generated.json', - 'utf8', - ); - - const snippetsArray = Object.entries(JSON.parse(snippets)) as [ - string, - Snippet, - ][]; + let snippetsArray: [string, Snippet][]; + try { + const snippets = await readFile( + path.join(__dirname, '..', 'snippets', 'generated.json'), + 'utf8', + ); + snippetsArray = Object.entries(JSON.parse(snippets)) as [string, Snippet][]; + } catch { + window.showErrorMessage( + 'React Snippets: Failed to load snippets. Try regenerating via settings change.', + ); + return; + } const items = snippetsArray.map( ([shortDescription, { body, description, prefix: label }]) => ({ @@ -34,7 +39,8 @@ const snippetSearch = async () => { const body = rawSnippet ? parseSnippet(rawSnippet.body) : ''; if (activeTextEditor) { - activeTextEditor.insertSnippet(new SnippetString(body)); + await activeTextEditor.insertSnippet(new SnippetString(body)); + await commands.executeCommand('editor.action.formatDocument'); } }; diff --git a/src/index.ts b/src/index.ts index a111de3..066d574 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,26 +15,25 @@ const showRestartMessage = async ({ }: ConfigurationChangeEvent) => { if (affectsConfiguration('reactSnippets')) { await generateSnippets(); - setTimeout(() => { - window - .showWarningMessage( - 'React Snippets: Please restart VS Code to apply snippet formatting changes', - 'Restart VS Code', - 'Ignore', - ) - .then((action?: string) => { - if (action === 'Restart VS Code') { - commands.executeCommand('workbench.action.reloadWindow'); - } - }); - }, 1000); + const action = await window.showWarningMessage( + 'React Snippets: Please restart VS Code to apply snippet formatting changes', + 'Restart VS Code', + 'Ignore', + ); + if (action === 'Restart VS Code') { + commands.executeCommand('workbench.action.reloadWindow'); + } } }; export async function activate(context: ExtensionContext) { workspace.onDidChangeConfiguration(showRestartMessage); - if (JSON.stringify(generatedSnippets).length < 10) { - await generateSnippets(); + if (Object.keys(generatedSnippets).length === 0) { + try { + await generateSnippets(); + } catch (error) { + console.error(error); + } } const snippetSearchCommand = commands.registerCommand( 'reactSnippets.search', diff --git a/src/snippets/generated.json b/src/snippets/generated.json index d73bea5..9fb8969 100644 --- a/src/snippets/generated.json +++ b/src/snippets/generated.json @@ -1,6 +1,8 @@ { "exportType": { - "body": ["export type ${1:first} = {${2:second}}"], + "body": [ + "export type ${1:first} = {${2:second}}" + ], "key": "exportType", "prefix": "exptp", "scope": "typescript,typescriptreact,javascript,javascriptreact" @@ -8,7 +10,9 @@ "exportInterface": { "key": "exportInterface", "prefix": "expint", - "body": ["export interface ${1:first} {${2:second}}"], + "body": [ + "export interface ${1:first} {${2:second}}" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "typescriptReactClassComponent": { @@ -16,7 +20,7 @@ "prefix": "tsrcc", "description": "Creates a React component class with ES7 module system and TypeScript interfaces", "body": [ - "import React, { Component } from 'react'", + "import { Component } from 'react'", "", "type Props = {}", "", @@ -27,7 +31,7 @@ "", " render() {", " return (", - "
${1:first}
", + " <>${1:first}", " )", " }", "}" @@ -38,7 +42,7 @@ "key": "typescriptReactClassExportComponent", "prefix": "tsrce", "body": [ - "import React, { Component } from 'react'", + "import { Component } from 'react'", "", "type Props = {}", "", @@ -49,7 +53,7 @@ "", " render() {", " return (", - "
${1:first}
", + " <>${1:first}", " )", " }", "}", @@ -63,16 +67,12 @@ "key": "typescriptReactFunctionalExportComponent", "prefix": "tsrfce", "body": [ - "import React from 'react'", - "", "type Props = {}", - "", "function ${1:${TM_FILENAME_BASE}}({}: Props) {", " return (", - "
${1:first}
", + " <>${1:first}", " )", "}", - "", "export default ${1:${TM_FILENAME_BASE}}" ], "description": "Creates a React Functional Component with ES7 module system and TypeScript interface", @@ -82,13 +82,10 @@ "key": "typescriptReactFunctionalComponent", "prefix": "tsrfc", "body": [ - "import React from 'react'", - "", "type Props = {}", - "", "export default function ${1:${TM_FILENAME_BASE}}({}: Props) {", " return (", - "
${1:first}
", + " <>${1:first}", " )", "}" ], @@ -99,16 +96,12 @@ "key": "typescriptReactArrowFunctionExportComponent", "prefix": "tsrafce", "body": [ - "import React from 'react'", - "", "type Props = {}", - "", "const ${1:${TM_FILENAME_BASE}} = (props: Props) => {", " return (", - "
${1:first}
", + " <>${1:first}", " )", "}", - "", "export default ${1:${TM_FILENAME_BASE}}" ], "description": "Creates a React Arrow Function Component with ES7 module system and TypeScript interface", @@ -118,13 +111,10 @@ "key": "typescriptReactArrowFunctionComponent", "prefix": "tsrafc", "body": [ - "import React from 'react'", - "", "type Props = {}", - "", "const ${1:${TM_FILENAME_BASE}} = (props: Props) => {", " return (", - "
${1:first}
", + " <>${1:first}", " )", "}" ], @@ -135,14 +125,14 @@ "key": "typescriptReactClassPureComponent", "prefix": "tsrpc", "body": [ - "import React, { PureComponent } from 'react'", + "import { PureComponent } from 'react'", "", "type Props = {}", "", "export default class ${1:${TM_FILENAME_BASE}} extends PureComponent {", " render() {", " return (", - "
${1:first}
", + " <>${1:first}", " )", " }", "}" @@ -154,14 +144,14 @@ "key": "typescriptReactClassExportPureComponent", "prefix": "tsrpce", "body": [ - "import React, { PureComponent } from 'react'", + "import { PureComponent } from 'react'", "", "type Props = {}", "", "class ${1:${TM_FILENAME_BASE}} extends PureComponent {", " render() {", " return (", - "
${1:first}
", + " <>${1:first}", " )", " }", "}", @@ -176,7 +166,7 @@ "prefix": "tsrcredux", "body": [ "import { connect } from 'react-redux'", - "import React, { Component } from 'react'", + "import { Component } from 'react'", "", "type Props = {}", "", @@ -187,7 +177,7 @@ "", " render() {", " return (", - "
${1:first}
", + " <>${1:first}", " )", " }", "}", @@ -206,10 +196,7 @@ "prefix": "tsrnf", "body": [ "import { View, Text } from 'react-native'", - "import React from 'react'", - "", "type Props = {}", - "", "const ${1:${TM_FILENAME_BASE}} = (props: Props) => {", " return (", " ", @@ -217,7 +204,6 @@ " ", " )", "}", - "", "export default ${1:${TM_FILENAME_BASE}}" ], "description": "Creates a React Native Arrow Function Component with ES7 module system in TypeScript", @@ -228,10 +214,7 @@ "prefix": "tsrnfs", "body": [ "import { StyleSheet, Text, View } from 'react-native'", - "import React from 'react'", - "", "type Props = {}", - "", "const ${1:${TM_FILENAME_BASE}} = (props: Props) => {", " return (", " ", @@ -239,9 +222,7 @@ " ", " )", "}", - "", "export default ${1:${TM_FILENAME_BASE}}", - "", "const styles = StyleSheet.create({})" ], "description": "Creates a React Native Arrow Function Component with ES7 module system, TypeScript interface and StyleSheet", @@ -251,14 +232,11 @@ "key": "reactArrowFunctionComponent", "prefix": "rafc", "body": [ - "import React from 'react'", - "", "export const ${1:${TM_FILENAME_BASE}} = () => {", " return (", - "
${1:first}
", + " <>${1:first}", " )", - "}", - "" + "}" ], "description": "Creates a React Arrow Function Component with ES7 module system", "scope": "typescript,typescriptreact,javascript,javascriptreact" @@ -267,17 +245,13 @@ "key": "reactArrowFunctionComponentWithPropTypes", "prefix": "rafcp", "body": [ - "import React from 'react'", "import PropTypes from 'prop-types'", - "", "const ${1:${TM_FILENAME_BASE}} = props => {", " return (", - "
${1:first}
", + " <>${1:first}", " )", "}", - "", "${1:${TM_FILENAME_BASE}}.propTypes = {}", - "", "export default ${1:${TM_FILENAME_BASE}}" ], "description": "Creates a React Arrow Function Component with ES7 module system with PropTypes", @@ -287,14 +261,11 @@ "key": "reactArrowFunctionExportComponent", "prefix": "rafce", "body": [ - "import React from 'react'", - "", "const ${1:${TM_FILENAME_BASE}} = () => {", " return (", - "
${1:first}
", + " <>${1:first}", " )", "}", - "", "export default ${1:${TM_FILENAME_BASE}}" ], "description": "Creates a React Arrow Function Component with ES7 module system", @@ -304,12 +275,12 @@ "key": "reactClassComponent", "prefix": "rcc", "body": [ - "import React, { Component } from 'react'", + "import { Component } from 'react'", "", "export default class ${1:${TM_FILENAME_BASE}} extends Component {", " render() {", " return (", - "
${1:first}
", + " <>${1:first}", " )", " }", "}", @@ -323,14 +294,14 @@ "prefix": "rccp", "body": [ "import PropTypes from 'prop-types'", - "import React, { Component } from 'react'", + "import { Component } from 'react'", "", "export default class ${1:${TM_FILENAME_BASE}} extends Component {", " static propTypes = {${2:second}: ${3:third}}", "", " render() {", " return (", - "
${1:first}
", + " <>${1:first}", " )", " }", "}", @@ -343,13 +314,13 @@ "key": "reactClassComponentRedux", "prefix": "rcredux", "body": [ - "import React, { Component } from 'react'", + "import { Component } from 'react'", "import { connect } from 'react-redux'", "", "export class ${1:${TM_FILENAME_BASE}} extends Component {", " render() {", " return (", - "
${1:first}
", + " <>${1:first}", " )", " }", "}", @@ -368,7 +339,7 @@ "prefix": "rcreduxp", "body": [ "import PropTypes from 'prop-types'", - "import React, { Component } from 'react'", + "import { Component } from 'react'", "import { connect } from 'react-redux'", "", "export class ${1:${TM_FILENAME_BASE}} extends Component {", @@ -378,7 +349,7 @@ "", " render() {", " return (", - "
${1:first}
", + " <>${1:first}", " )", " }", "}", @@ -396,12 +367,12 @@ "key": "reactClassExportComponent", "prefix": "rce", "body": [ - "import React, { Component } from 'react'", + "import { Component } from 'react'", "", "export class ${1:${TM_FILENAME_BASE}} extends Component {", " render() {", " return (", - "
${1:first}
", + " <>${1:first}", " )", " }", "}", @@ -416,14 +387,14 @@ "prefix": "rcep", "body": [ "import PropTypes from 'prop-types'", - "import React, { Component } from 'react'", + "import { Component } from 'react'", "", "export class ${1:${TM_FILENAME_BASE}} extends Component {", " static propTypes = {}", "", " render() {", " return (", - "
${1:first}
", + " <>${1:first}", " )", " }", "}", @@ -437,12 +408,12 @@ "key": "reactClassExportPureComponent", "prefix": "rpce", "body": [ - "import React, { PureComponent } from 'react'", + "import { PureComponent } from 'react'", "", "export class ${1:${TM_FILENAME_BASE}} extends PureComponent {", " render() {", " return (", - "
${1:first}
", + " <>${1:first}", " )", " }", "}", @@ -456,12 +427,12 @@ "key": "reactClassPureComponent", "prefix": "rpc", "body": [ - "import React, { PureComponent } from 'react'", + "import { PureComponent } from 'react'", "", "export default class ${1:${TM_FILENAME_BASE}} extends PureComponent {", " render() {", " return (", - "
${1:first}
", + " <>${1:first}", " )", " }", "}", @@ -475,14 +446,14 @@ "prefix": "rpcp", "body": [ "import PropTypes from 'prop-types'", - "import React, { PureComponent } from 'react'", + "import { PureComponent } from 'react'", "", "export default class ${1:${TM_FILENAME_BASE}} extends PureComponent {", " static propTypes = {}", "", " render() {", " return (", - "
${1:first}
", + " <>${1:first}", " )", " }", "}", @@ -495,11 +466,11 @@ "key": "reactFunctionMemoComponent", "prefix": "rmc", "body": [ - "import React, { memo } from 'react'", + "import { memo } from 'react'", "", "const ${1:${TM_FILENAME_BASE}} = memo(() => {", " return (", - "
${1:first}
", + " <>${1:first}", " )", "})", "", @@ -513,11 +484,11 @@ "prefix": "rmcp", "body": [ "import PropTypes from 'prop-types'", - "import React, { memo } from 'react'", + "import { memo } from 'react'", "", "const ${1:${TM_FILENAME_BASE}} = memo((props) => {", " return (", - "
${1:first}
", + " <>${1:first}", " )", "})", "", @@ -532,14 +503,11 @@ "key": "reactFunctionalComponent", "prefix": "rfc", "body": [ - "import React from 'react'", - "", "export default function ${1:${TM_FILENAME_BASE}}() {", " return (", - "
${1:first}
", + " <>${1:first}", " )", - "}", - "" + "}" ], "description": "Creates a React Functional Component with ES7 module system", "scope": "typescript,typescriptreact,javascript,javascriptreact" @@ -548,68 +516,31 @@ "key": "reactFunctionalComponentRedux", "prefix": "rfcredux", "body": [ - "import React from 'react'", "import { connect } from 'react-redux'", - "", "export const ${1:${TM_FILENAME_BASE}} = (props) => {", " return (", - "
${1:first}
", + " <>${1:first}", " )", "}", - "", "const mapStateToProps = (state) => ({})", - "", "const mapDispatchToProps = {}", - "", "export default connect(mapStateToProps, mapDispatchToProps)(${1:${TM_FILENAME_BASE}})" ], "description": "Creates a React functional component with connected redux and ES7 module system", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, - "reactFunctionalComponentReduxPropTypes": { - "key": "reactFunctionalComponentReduxPropTypes", - "prefix": "rfcreduxp", - "body": [ - "import PropTypes from 'prop-types'", - "import React from 'react'", - "import { connect } from 'react-redux'", - "", - "export const ${1:${TM_FILENAME_BASE}} = (props) => {", - " return (", - "
${1:first}
", - " )", - "}", - "", - "${1:${TM_FILENAME_BASE}}.propTypes = {", - " ${2:second}: PropTypes.${3:third}", - "}", - "", - "const mapStateToProps = (state) => ({})", - "", - "const mapDispatchToProps = {}", - "", - "export default connect(mapStateToProps, mapDispatchToProps)(${1:${TM_FILENAME_BASE}})" - ], - "description": "DEPRECATED: Creates a React functional component with PropTypes with connected redux and ES7 module system", - "scope": "typescript,typescriptreact,javascript,javascriptreact" - }, "reactFunctionalComponentWithPropTypes": { "key": "reactFunctionalComponentWithPropTypes", "prefix": "rfcp", "body": [ - "import React from 'react'", "import PropTypes from 'prop-types'", - "", "function ${1:${TM_FILENAME_BASE}}(props) {", " return (", - "
${1:first}
", + " <>${1:first}", " )", "}", - "", "${1:${TM_FILENAME_BASE}}.propTypes = {}", - "", - "export default ${1:${TM_FILENAME_BASE}}", - "" + "export default ${1:${TM_FILENAME_BASE}}" ], "description": "Creates a React Functional Component with ES7 module system with PropTypes", "scope": "typescript,typescriptreact,javascript,javascriptreact" @@ -618,14 +549,11 @@ "key": "reactFunctionalExportComponent", "prefix": "rfce", "body": [ - "import React from 'react'", - "", "function ${1:${TM_FILENAME_BASE}}() {", " return (", - "
${1:first}
", + " <>${1:first}", " )", "}", - "", "export default ${1:${TM_FILENAME_BASE}}" ], "description": "Creates a React Functional Component with ES7 module system", @@ -634,115 +562,169 @@ "consoleAssert": { "key": "consoleAssert", "prefix": "cas", - "body": ["console.assert(${1:first}, ${2:second})"], + "body": [ + "console.assert(${1:first}, ${2:second})" + ], "description": "If the specified expression is false, the message is written to the console along with a stack trace", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "consoleClear": { "key": "consoleClear", "prefix": "ccl", - "body": ["console.clear()"], + "body": [ + "console.clear()" + ], "description": "Clears the console", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "consoleCount": { "key": "consoleCount", "prefix": "cco", - "body": ["console.count(${1:first})"], + "body": [ + "console.count(${1:first})" + ], "description": "Writes the the number of times that count() has been invoked at the same line and with the same label", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "consoleDir": { "key": "consoleDir", "prefix": "cdi", - "body": ["console.dir(${1:first})"], + "body": [ + "console.dir(${1:first})" + ], "description": "Prints a JavaScript representation of the specified object", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "consoleError": { "key": "consoleError", "prefix": "cer", - "body": ["console.error(${1:first})"], + "body": [ + "console.error(${1:first})" + ], "description": "Displays a message in the console and also includes a stack trace from where the method was called", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "consoleGroup": { "key": "consoleGroup", "prefix": "cgr", - "body": ["console.group('${1:first}')"], + "body": [ + "console.group('${1:first}')" + ], "description": "Groups and indents all following output by an additional level, until console.groupEnd() is called.", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "consoleGroupEnd": { "key": "consoleGroupEnd", "prefix": "cge", - "body": ["console.groupEnd()"], + "body": [ + "console.groupEnd()" + ], "description": "Closes out the corresponding console.group().", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "consoleLog": { "key": "consoleLog", "prefix": "clg", - "body": ["console.log(${1:first})"], + "body": [ + "console.log(${1:first})" + ], "description": "Displays a message in the console", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "consoleTrace": { "key": "consoleTrace", "prefix": "ctr", - "body": ["console.trace(${1:first})"], + "body": [ + "console.trace(${1:first})" + ], "description": "Prints a stack trace from the point where the method was called", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "consoleLogObject": { "key": "consoleLogObject", "prefix": "clo", - "body": ["console.log('${1:first}', ${1:first})"], + "body": [ + "console.log('${1:first}', ${1:first})" + ], "description": "Logs property with name.", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "consoleLogJson": { "key": "consoleLogJson", "prefix": "clj", - "body": ["console.log('${1:first}', JSON.stringify(${1:first}, null, 2))"], + "body": [ + "console.log('${1:first}', JSON.stringify(${1:first}, null, 2))" + ], "description": "Logs stringified JSON property with name.", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "consoleTime": { "key": "consoleTime", "prefix": "ctm", - "body": ["console.time('${1:first}')"], + "body": [ + "console.time('${1:first}')" + ], "description": "Console time wrapper", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "consoleTimeEnd": { "key": "consoleTimeEnd", "prefix": "cte", - "body": ["console.timeEnd('${1:first}')"], + "body": [ + "console.timeEnd('${1:first}')" + ], "description": "Console time end wrapper", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "consoleWarn": { "key": "consoleWarn", "prefix": "cwa", - "body": ["console.warn(${1:first})"], + "body": [ + "console.warn(${1:first})" + ], "description": "Displays a message in the console but also displays a yellow warning icon along with the logged message", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "consoleInfo": { "key": "consoleInfo", "prefix": "cin", - "body": ["console.info(${1:first})"], + "body": [ + "console.info(${1:first})" + ], "description": "Displays a message in the console but also displays a blue information icon along with the logged message", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "consoleTable": { "key": "consoleTable", "prefix": "ctl", - "body": ["console.table([${1:first}])"], + "body": [ + "console.table([${1:first}])" + ], "description": "Logs table to console", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, + "use": { + "key": "use", + "prefix": "useSnippet", + "body": [ + "const ${1:first} = use(${2:second})" + ], + "description": "Read a Promise or Context in render (React 19)", + "scope": "typescript,typescriptreact,javascript,javascriptreact" + }, + "useActionState": { + "key": "useActionState", + "prefix": "useActionStateSnippet", + "body": [ + "const [state, submitAction, isPending] = useActionState(", + " async (previousState, formData) => {", + " ${1:first}", + " },", + " ${2:second},", + ")" + ], + "scope": "typescript,typescriptreact,javascript,javascriptreact" + }, "useCallback": { "key": "useCallback", "prefix": "useCallbackSnippet", @@ -760,7 +742,17 @@ "useContext": { "key": "useContext", "prefix": "useContextSnippet", - "body": ["const ${1:first} = useContext(${2:second})"], + "body": [ + "const ${1:first} = useContext(${2:second})" + ], + "scope": "typescript,typescriptreact,javascript,javascriptreact" + }, + "useDeferredValue": { + "key": "useDeferredValue", + "prefix": "useDeferredValueSnippet", + "body": [ + "const ${1:first} = useDeferredValue(${2:second})" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "useEffect": { @@ -778,6 +770,23 @@ ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, + "useFormStatus": { + "key": "useFormStatus", + "prefix": "useFormStatusSnippet", + "body": [ + "const { pending, data, method, action } = useFormStatus()" + ], + "description": "useFormStatus (import from react-dom)", + "scope": "typescript,typescriptreact,javascript,javascriptreact" + }, + "useId": { + "key": "useId", + "prefix": "useIdSnippet", + "body": [ + "const ${1:first} = useId()" + ], + "scope": "typescript,typescriptreact,javascript,javascriptreact" + }, "useImperativeHandle": { "key": "useImperativeHandle", "prefix": "useImperativeHandleSnippet", @@ -809,7 +818,20 @@ "useMemo": { "key": "useMemo", "prefix": "useMemoSnippet", - "body": ["useMemo(() => ${1:first}, [${2:second}])"], + "body": [ + "useMemo(() => ${1:first}, [${2:second}])" + ], + "scope": "typescript,typescriptreact,javascript,javascriptreact" + }, + "useOptimistic": { + "key": "useOptimistic", + "prefix": "useOptimisticSnippet", + "body": [ + "const [optimistic${1/(.*)/${1:/capitalize}/}, addOptimistic] = useOptimistic(", + " ${1:first},", + " (state, newValue) => [...state, newValue],", + ")" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "useReducer": { @@ -823,7 +845,9 @@ "useRef": { "key": "useRef", "prefix": "useRefSnippet", - "body": ["const ${1:first} = useRef(${2:second})"], + "body": [ + "const ${1:first} = useRef(${2:second})" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "useState": { @@ -834,16 +858,28 @@ ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, + "useTransition": { + "key": "useTransition", + "prefix": "useTransitionSnippet", + "body": [ + "const [isPending, startTransition] = useTransition()" + ], + "scope": "typescript,typescriptreact,javascript,javascriptreact" + }, "importAs": { "key": "importAs", "prefix": "ima", - "body": ["import { ${2:second} as ${3:third} } from '${1:first}'"], + "body": [ + "import { ${2:second} as ${3:third} } from '${1:first}'" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "importBrowserRouter": { "key": "importBrowserRouter", "prefix": "imbr", - "body": ["import { BrowserRouter as Router } from 'react-router-dom'"], + "body": [ + "import { BrowserRouter as Router } from 'react-router-dom'" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "importBrowserRouterWithRouteAndNavLink": { @@ -858,50 +894,64 @@ "importDestructing": { "key": "importDestructing", "prefix": "imd", - "body": ["import { ${2:second} } from '${1:first}'"], + "body": [ + "import { ${2:second} } from '${1:first}'" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "importEverything": { "key": "importEverything", "prefix": "ime", - "body": ["import * as ${2:second} from '${1:first}'"], + "body": [ + "import * as ${2:second} from '${1:first}'" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "importNoModuleName": { "key": "importNoModuleName", "prefix": "imn", - "body": ["import '${1:first}'"], + "body": [ + "import '${1:first}'" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "importPropTypes": { "key": "importPropTypes", "prefix": "impt", - "body": ["import PropTypes from 'prop-types'"], + "body": [ + "import PropTypes from 'prop-types'" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "importReact": { "key": "importReact", "prefix": "imr", - "body": ["import React from 'react'"], + "body": [ + "" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "importReactDom": { "key": "importReactDom", "prefix": "imrd", - "body": ["import ReactDOM from 'react-dom'"], + "body": [ + "import ReactDOM from 'react-dom'" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "importReactWithComponent": { "key": "importReactWithComponent", "prefix": "imrc", - "body": ["import React, { Component } from 'react'"], + "body": [ + "import { Component } from 'react'" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "importReactWithComponentAndPropTypes": { "key": "importReactWithComponentAndPropTypes", "prefix": "imrcp", "body": [ - "import React, { Component } from 'react'", + "import { Component } from 'react'", "import PropTypes from 'prop-types'", "" ], @@ -910,14 +960,16 @@ "importReactWithMemo": { "key": "importReactWithMemo", "prefix": "imrm", - "body": ["import React, { memo } from 'react'"], + "body": [ + "import { memo } from 'react'" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "importReactWithMemoAndPropTypes": { "key": "importReactWithMemoAndPropTypes", "prefix": "imrmp", "body": [ - "import React, { memo } from 'react'", + "import { memo } from 'react'", "import PropTypes from 'prop-types'", "" ], @@ -926,263 +978,397 @@ "importReactWithPureComponent": { "key": "importReactWithPureComponent", "prefix": "imrpc", - "body": ["import React, { PureComponent } from 'react'"], + "body": [ + "import { PureComponent } from 'react'" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "importReactWithPureComponentAndPropTypes": { "key": "importReactWithPureComponentAndPropTypes", "prefix": "imrpcp", "body": [ - "import React, { PureComponent } from 'react'", + "import { PureComponent } from 'react'", "import PropTypes from 'prop-types'", "" ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, + "importCreateBrowserRouter": { + "key": "importCreateBrowserRouter", + "prefix": "imcbr", + "body": [ + "import { createBrowserRouter, RouterProvider } from 'react-router-dom'" + ], + "scope": "typescript,typescriptreact,javascript,javascriptreact" + }, "importRouterLink": { "key": "importRouterLink", "prefix": "imbrl", - "body": ["import { Link } from 'react-router-dom'"], + "body": [ + "import { Link } from 'react-router-dom'" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "importRouterNavLink": { "key": "importRouterNavLink", "prefix": "imbrnl", - "body": ["import { NavLink } from 'react-router-dom'"], + "body": [ + "import { NavLink } from 'react-router-dom'" + ], + "scope": "typescript,typescriptreact,javascript,javascriptreact" + }, + "importRouterRoutes": { + "key": "importRouterRoutes", + "prefix": "imrrs", + "body": [ + "import { Routes, Route } from 'react-router-dom'" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "importRouterSetup": { "key": "importRouterSetup", "prefix": "imbrc", - "body": ["import { Route, Switch, NavLink, Link } from 'react-router-dom'"], - "scope": "typescript,typescriptreact,javascript,javascriptreact" - }, - "importRouterSwitch": { - "key": "importRouterSwitch", - "prefix": "imbrs", - "body": ["import { Switch } from 'react-router-dom'"], + "body": [ + "import { Routes, Route, NavLink, Link } from 'react-router-dom'" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "import": { "key": "import", "prefix": "imp", - "body": ["import ${2:second} from '${1:first}'"], + "body": [ + "import ${2:second} from '${1:first}'" + ], + "scope": "typescript,typescriptreact,javascript,javascriptreact" + }, + "importUseFetcher": { + "key": "importUseFetcher", + "prefix": "imfet", + "body": [ + "import { useFetcher } from 'react-router-dom'" + ], + "scope": "typescript,typescriptreact,javascript,javascriptreact" + }, + "importUseLoaderData": { + "key": "importUseLoaderData", + "prefix": "imld", + "body": [ + "import { useLoaderData } from 'react-router-dom'" + ], + "scope": "typescript,typescriptreact,javascript,javascriptreact" + }, + "importUseNavigate": { + "key": "importUseNavigate", + "prefix": "imnav", + "body": [ + "import { useNavigate } from 'react-router-dom'" + ], + "scope": "typescript,typescriptreact,javascript,javascriptreact" + }, + "importUseParams": { + "key": "importUseParams", + "prefix": "impar", + "body": [ + "import { useParams } from 'react-router-dom'" + ], + "scope": "typescript,typescriptreact,javascript,javascriptreact" + }, + "importUseSearchParams": { + "key": "importUseSearchParams", + "prefix": "imsp", + "body": [ + "import { useSearchParams } from 'react-router-dom'" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeArray": { "key": "propTypeArray", "prefix": "pta", - "body": ["PropTypes.array"], + "body": [ + "PropTypes.array" + ], "description": "Array prop type", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeArrayRequired": { "key": "propTypeArrayRequired", "prefix": "ptar", - "body": ["PropTypes.array.isRequired"], + "body": [ + "PropTypes.array.isRequired" + ], "description": "Array prop type required", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeBool": { "key": "propTypeBool", "prefix": "ptb", - "body": ["PropTypes.bool"], + "body": [ + "PropTypes.bool" + ], "description": "Bool prop type", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeBoolRequired": { "key": "propTypeBoolRequired", "prefix": "ptbr", - "body": ["PropTypes.bool.isRequired"], + "body": [ + "PropTypes.bool.isRequired" + ], "description": "Bool prop type required", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeFunc": { "key": "propTypeFunc", "prefix": "ptf", - "body": ["PropTypes.func"], + "body": [ + "PropTypes.func" + ], "description": "Func prop type", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeFuncRequired": { "key": "propTypeFuncRequired", "prefix": "ptfr", - "body": ["PropTypes.func.isRequired"], + "body": [ + "PropTypes.func.isRequired" + ], "description": "Func prop type required", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeNumber": { "key": "propTypeNumber", "prefix": "ptn", - "body": ["PropTypes.number"], + "body": [ + "PropTypes.number" + ], "description": "Number prop type", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeNumberRequired": { "key": "propTypeNumberRequired", "prefix": "ptnr", - "body": ["PropTypes.number.isRequired"], + "body": [ + "PropTypes.number.isRequired" + ], "description": "Number prop type required", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeObject": { "key": "propTypeObject", "prefix": "pto", - "body": ["PropTypes.object"], + "body": [ + "PropTypes.object" + ], "description": "Object prop type", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeObjectRequired": { "key": "propTypeObjectRequired", "prefix": "ptor", - "body": ["PropTypes.object.isRequired"], + "body": [ + "PropTypes.object.isRequired" + ], "description": "Object prop type required", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeString": { "key": "propTypeString", "prefix": "pts", - "body": ["PropTypes.string"], + "body": [ + "PropTypes.string" + ], "description": "String prop type", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeStringRequired": { "key": "propTypeStringRequired", "prefix": "ptsr", - "body": ["PropTypes.string.isRequired"], + "body": [ + "PropTypes.string.isRequired" + ], "description": "String prop type required", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeNode": { "key": "propTypeNode", "prefix": "ptnd", - "body": ["PropTypes.node"], + "body": [ + "PropTypes.node" + ], "description": "Anything that can be rendered: numbers, strings, elements or an array", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeNodeRequired": { "key": "propTypeNodeRequired", "prefix": "ptndr", - "body": ["PropTypes.node.isRequired"], + "body": [ + "PropTypes.node.isRequired" + ], "description": "Anything that can be rendered: numbers, strings, elements or an array required", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeElement": { "key": "propTypeElement", "prefix": "ptel", - "body": ["PropTypes.element"], + "body": [ + "PropTypes.element" + ], "description": "React element prop type", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeElementRequired": { "key": "propTypeElementRequired", "prefix": "ptelr", - "body": ["PropTypes.element.isRequired"], + "body": [ + "PropTypes.element.isRequired" + ], "description": "React element prop type required", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeInstanceOf": { "key": "propTypeInstanceOf", "prefix": "pti", - "body": ["PropTypes.instanceOf($0)"], + "body": [ + "PropTypes.instanceOf($0)" + ], "description": "Is an instance of a class prop type", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeInstanceOfRequired": { "key": "propTypeInstanceOfRequired", "prefix": "ptir", - "body": ["PropTypes.instanceOf($0).isRequired"], + "body": [ + "PropTypes.instanceOf($0).isRequired" + ], "description": "Is an instance of a class prop type required", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeEnum": { "key": "propTypeEnum", "prefix": "pte", - "body": ["PropTypes.oneOf(['$0'])"], + "body": [ + "PropTypes.oneOf(['$0'])" + ], "description": "Prop type limited to specific values by treating it as an enum", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeEnumRequired": { "key": "propTypeEnumRequired", "prefix": "pter", - "body": ["PropTypes.oneOf(['$0']).isRequired"], + "body": [ + "PropTypes.oneOf(['$0']).isRequired" + ], "description": "Prop type limited to specific values by treating it as an enum required", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeOneOfType": { "key": "propTypeOneOfType", "prefix": "ptet", - "body": ["PropTypes.oneOfType([", " $0", "])"], + "body": [ + "PropTypes.oneOfType([", + " $0", + "])" + ], "description": "An object that could be one of many types", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeOneOfTypeRequired": { "key": "propTypeOneOfTypeRequired", "prefix": "ptetr", - "body": ["PropTypes.oneOfType([", " $0", "]).isRequired"], + "body": [ + "PropTypes.oneOfType([", + " $0", + "]).isRequired" + ], "description": "An object that could be one of many types required", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeArrayOf": { "key": "propTypeArrayOf", "prefix": "ptao", - "body": ["PropTypes.arrayOf($0)"], + "body": [ + "PropTypes.arrayOf($0)" + ], "description": "An array of a certain type", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeArrayOfRequired": { "key": "propTypeArrayOfRequired", "prefix": "ptaor", - "body": ["PropTypes.arrayOf($0).isRequired"], + "body": [ + "PropTypes.arrayOf($0).isRequired" + ], "description": "An array of a certain type required", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeObjectOf": { "key": "propTypeObjectOf", "prefix": "ptoo", - "body": ["PropTypes.objectOf($0)"], + "body": [ + "PropTypes.objectOf($0)" + ], "description": "An object with property values of a certain type", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeObjectOfRequired": { "key": "propTypeObjectOfRequired", "prefix": "ptoor", - "body": ["PropTypes.objectOf($0).isRequired"], + "body": [ + "PropTypes.objectOf($0).isRequired" + ], "description": "An object with property values of a certain type required", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeShape": { "key": "propTypeShape", "prefix": "ptsh", - "body": ["PropTypes.shape({", " $0", "})"], + "body": [ + "PropTypes.shape({", + " $0", + "})" + ], "description": "An object taking on a particular shape", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeShapeRequired": { "key": "propTypeShapeRequired", "prefix": "ptshr", - "body": ["PropTypes.shape({", " $0", "}).isRequired"], + "body": [ + "PropTypes.shape({", + " $0", + "}).isRequired" + ], "description": "An object taking on a particular shape required", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeExact": { "key": "propTypeExact", "prefix": "ptex", - "body": ["PropTypes.exact({", " $0", "})"], + "body": [ + "PropTypes.exact({", + " $0", + "})" + ], "description": "An object with warnings on extra properties", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeExactRequired": { "key": "propTypeExactRequired", "prefix": "ptexr", - "body": ["PropTypes.exact({", " $0", "}).isRequired"], + "body": [ + "PropTypes.exact({", + " $0", + "}).isRequired" + ], "description": "An object with warnings on extra properties required", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propTypeAny": { "key": "propTypeAny", "prefix": "ptany", - "body": ["PropTypes.any"], + "body": [ + "PropTypes.any" + ], "description": "Any prop type", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, @@ -1191,7 +1377,7 @@ "prefix": "rnc", "body": [ "import { Text, View } from 'react-native'", - "import React, { Component } from 'react'", + "import { Component } from 'react'", "", "export default class ${1:${TM_FILENAME_BASE}} extends Component {", " render() {", @@ -1210,7 +1396,7 @@ "prefix": "rnce", "body": [ "import { Text, View } from 'react-native'", - "import React, { Component } from 'react'", + "import { Component } from 'react'", "", "export class ${1:${TM_FILENAME_BASE}} extends Component {", " render() {", @@ -1231,7 +1417,7 @@ "prefix": "rncs", "body": [ "import { Text, StyleSheet, View } from 'react-native'", - "import React, { Component } from 'react'", + "import { Component } from 'react'", "", "export default class ${1:${TM_FILENAME_BASE}} extends Component {", " render() {", @@ -1252,8 +1438,6 @@ "prefix": "rnf", "body": [ "import { View, Text } from 'react-native'", - "import React from 'react'", - "", "export default function ${1:${TM_FILENAME_BASE}}() {", " return (", " ", @@ -1269,8 +1453,6 @@ "prefix": "rnfs", "body": [ "import { StyleSheet, Text, View } from 'react-native'", - "import React from 'react'", - "", "export default function ${1:${TM_FILENAME_BASE}}() {", " return (", " ", @@ -1278,7 +1460,6 @@ " ", " )", "}", - "", "const styles = StyleSheet.create({})" ], "scope": "typescript,typescriptreact,javascript,javascriptreact" @@ -1288,8 +1469,6 @@ "prefix": "rnfe", "body": [ "import { View, Text } from 'react-native'", - "import React from 'react'", - "", "const ${1:${TM_FILENAME_BASE}} = () => {", " return (", " ", @@ -1297,7 +1476,6 @@ " ", " )", "}", - "", "export default ${1:${TM_FILENAME_BASE}}" ], "scope": "typescript,typescriptreact,javascript,javascriptreact" @@ -1307,8 +1485,6 @@ "prefix": "rnfes", "body": [ "import { StyleSheet, Text, View } from 'react-native'", - "import React from 'react'", - "", "const ${1:${TM_FILENAME_BASE}} = () => {", " return (", " ", @@ -1316,9 +1492,7 @@ " ", " )", "}", - "", "export default ${1:${TM_FILENAME_BASE}}", - "", "const styles = StyleSheet.create({})" ], "scope": "typescript,typescriptreact,javascript,javascriptreact" @@ -1326,7 +1500,9 @@ "reactNativeImport": { "key": "reactNativeImport", "prefix": "imrn", - "body": ["import { ${1:first} } from 'react-native'"], + "body": [ + "import { ${1:first} } from 'react-native'" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "reactNativePureComponent": { @@ -1334,7 +1510,7 @@ "prefix": "rnpc", "body": [ "import { Text, View } from 'react-native'", - "import React, { PureComponent } from 'react'", + "import { PureComponent } from 'react'", "", "export default class ${1:${TM_FILENAME_BASE}} extends PureComponent {", " render() {", @@ -1353,7 +1529,7 @@ "prefix": "rnpce", "body": [ "import { Text, View } from 'react-native'", - "import React, { PureComponent } from 'react'", + "import { PureComponent } from 'react'", "", "export class ${1:${TM_FILENAME_BASE}} extends PureComponent {", " render() {", @@ -1372,13 +1548,17 @@ "reactNativeStyles": { "key": "reactNativeStyles", "prefix": "rnstyle", - "body": ["const styles = StyleSheet.create({${1:first}})"], + "body": [ + "const styles = StyleSheet.create({${1:first}})" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "importReduxConnect": { "key": "importReduxConnect", "prefix": "redux", - "body": ["import { connect } from 'react-redux'"], + "body": [ + "import { connect } from 'react-redux'" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "reduxAction": { @@ -1393,10 +1573,44 @@ ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, + "reduxApi": { + "key": "reduxApi", + "prefix": "rxapi", + "body": [ + "import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'", + "", + "export const ${1:first}Api = createApi({", + " reducerPath: '${1:first}Api',", + " baseQuery: fetchBaseQuery({ baseUrl: '${2:second}' }),", + " endpoints: (builder) => ({", + " ${3:third}: builder.query({", + " query: () => '/',", + " }),", + " }),", + "})", + "", + "export const { } = ${1:first}Api" + ], + "scope": "typescript,typescriptreact,javascript,javascriptreact" + }, + "reduxAsyncThunk": { + "key": "reduxAsyncThunk", + "prefix": "rxthunk", + "body": [ + "import { createAsyncThunk } from '@reduxjs/toolkit'", + "", + "export const ${1:first} = createAsyncThunk('${2:second}', async () => {", + " ${3:third}", + "})" + ], + "scope": "typescript,typescriptreact,javascript,javascriptreact" + }, "reduxConst": { "key": "reduxConst", "prefix": "rxconst", - "body": ["export const ${1:first} = '${1:first}'"], + "body": [ + "export const ${1:first} = '${1:first}'" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "reduxReducer": { @@ -1451,6 +1665,41 @@ ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, + "reduxSliceWithExtraReducers": { + "key": "reduxSliceWithExtraReducers", + "prefix": "rxslicex", + "body": [ + "import { createSlice } from '@reduxjs/toolkit'", + "", + "const initialState = {", + " ${1:first}", + " status: 'idle',", + "}", + "", + "const ${1:${TM_FILENAME_BASE}} = createSlice({", + " name: '${2:second}',", + " initialState,", + " reducers: {},", + " extraReducers: (builder) => {", + " builder", + " .addCase(${3:third}.pending, (state) => {", + " state.status = 'loading'", + " })", + " .addCase(${3:third}.fulfilled, (state, action) => {", + " state.status = 'succeeded'", + " })", + " .addCase(${3:third}.rejected, (state) => {", + " state.status = 'failed'", + " })", + " },", + "})", + "", + "export const {} = ${1:${TM_FILENAME_BASE}}.actions", + "", + "export default ${1:${TM_FILENAME_BASE}}.reducer" + ], + "scope": "typescript,typescriptreact,javascript,javascriptreact" + }, "mappingToProps": { "key": "mappingToProps", "prefix": "reduxmap", @@ -1464,21 +1713,27 @@ "describeBlock": { "key": "describeBlock", "prefix": "desc", - "body": ["describe('${1:first}', () => { ${2:second} })"], + "body": [ + "describe('${1:first}', () => { ${2:second} })" + ], "description": "Testing `describe` block", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "itAsyncBlock": { "key": "itAsyncBlock", "prefix": "tita", - "body": ["it('should ${1:first}', async () => { ${2:second} })"], + "body": [ + "it('should ${1:first}', async () => { ${2:second} })" + ], "description": "Testing asynchronous `it` block", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "itBlock": { "key": "itBlock", "prefix": "tit", - "body": ["it('should ${1:first}', () => { ${2:second} })"], + "body": [ + "it('should ${1:first}', () => { ${2:second} })" + ], "description": "Testing `it` block", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, @@ -1486,13 +1741,10 @@ "key": "setupReactComponentTestWithRedux", "prefix": "srtest", "body": [ - "import React from 'react'", "import renderer from 'react-test-renderer'", "import { Provider } from 'react-redux'", - "", "import store from '~/store'", "import { ${1:${TM_FILENAME_BASE}} } from '../${1:${TM_FILENAME_BASE}}'", - "", "describe('<${1:${TM_FILENAME_BASE}} />', () => {", " const defaultProps = {}", " const wrapper = renderer.create(", @@ -1500,7 +1752,6 @@ " <${1:${TM_FILENAME_BASE}} {...defaultProps} />", " ,", " )", - "", " test('render', () => {", " expect(wrapper).toMatchSnapshot()", " })", @@ -1514,15 +1765,11 @@ "prefix": "sntest", "body": [ "import 'react-native'", - "import React from 'react'", "import renderer from 'react-test-renderer'", - "", "import ${1:${TM_FILENAME_BASE}} from '../${1:${TM_FILENAME_BASE}}'", - "", "describe('<${1:${TM_FILENAME_BASE}} />', () => {", " const defaultProps = {}", " const wrapper = renderer.create(<${1:${TM_FILENAME_BASE}} {...defaultProps} />)", - "", " test('render', () => {", " expect(wrapper).toMatchSnapshot()", " })", @@ -1535,13 +1782,10 @@ "prefix": "snrtest", "body": [ "import 'react-native'", - "import React from 'react'", "import renderer from 'react-test-renderer'", "import { Provider } from 'react-redux'", - "", "import store from '~/store'", "import ${1:${TM_FILENAME_BASE}} from '../${1:${TM_FILENAME_BASE}}'", - "", "describe('<${1:${TM_FILENAME_BASE}} />', () => {", " const defaultProps = {}", " const wrapper = renderer.create(", @@ -1549,7 +1793,6 @@ " <${1:${TM_FILENAME_BASE}} {...defaultProps} />", " ,", " )", - "", " test('render', () => {", " expect(wrapper).toMatchSnapshot()", " })", @@ -1561,15 +1804,11 @@ "key": "setupReactTest", "prefix": "stest", "body": [ - "import React from 'react'", "import renderer from 'react-test-renderer'", - "", "import { ${1:${TM_FILENAME_BASE}} } from '../${1:${TM_FILENAME_BASE}}'", - "", "describe('<${1:${TM_FILENAME_BASE}} />', () => {", " const defaultProps = {}", " const wrapper = renderer.create(<${1:${TM_FILENAME_BASE}} {...defaultProps} />)", - "", " test('render', () => {", " expect(wrapper).toMatchSnapshot()", " })", @@ -1580,158 +1819,206 @@ "testAsyncBlock": { "key": "testAsyncBlock", "prefix": "testa", - "body": ["test('should ${1:first}', async () => { ${2:second} })"], + "body": [ + "test('should ${1:first}', async () => { ${2:second} })" + ], "description": "Testing `asynchronous test` block", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "testBlock": { "key": "testBlock", "prefix": "test", - "body": ["test('should ${1:first}', () => { ${2:second} })"], + "body": [ + "test('should ${1:first}', () => { ${2:second} })" + ], "description": "Testing `test` block", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "exportDefault": { "key": "exportDefault", "prefix": "exp", - "body": ["export default ${1:first}"], + "body": [ + "export default ${1:first}" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "exportDestructing": { "key": "exportDestructing", "prefix": "exd", - "body": ["export { ${2:second} } from '${1:first}'"], + "body": [ + "export { ${2:second} } from '${1:first}'" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "exportAs": { "key": "exportAs", "prefix": "exa", - "body": ["export { ${2:second} as ${3:third} } from '${1:first}'"], + "body": [ + "export { ${2:second} as ${3:third} } from '${1:first}'" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "exportNamedFunction": { "key": "exportNamedFunction", "prefix": "enf", - "body": ["export const ${1:first} = (${2:second}) => {${3:third}}"], + "body": [ + "export const ${1:first} = (${2:second}) => {${3:third}}" + ], "description": "Export named function", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "exportDefaultFunction": { "key": "exportDefaultFunction", "prefix": "edf", - "body": ["export default (${1:first}) => {${2:second}}"], + "body": [ + "export default (${1:first}) => {${2:second}}" + ], "description": "Export default function", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "exportDefaultNamedFunction": { "key": "exportDefaultNamedFunction", "prefix": "ednf", - "body": ["export default function ${1:first}(${2:second}) {${3:third}}"], + "body": [ + "export default function ${1:first}(${2:second}) {${3:third}}" + ], "description": "Export default named function", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "method": { "key": "method", "prefix": "met", - "body": ["${1:first} = (${2:second}) => {${3:third}}"], + "body": [ + "${1:first} = (${2:second}) => {${3:third}}" + ], "description": "Creates a method inside a class", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propertyGet": { "key": "propertyGet", "prefix": "pge", - "body": ["get ${1:first}() {", " return this.${2:second}", "}"], + "body": [ + "get ${1:first}() {", + " return this.${2:second}", + "}" + ], "description": "Creates a getter property inside a class", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "propertySet": { "key": "propertySet", "prefix": "pse", - "body": ["set ${1:first}(${2:second}) {${3:third}}"], + "body": [ + "set ${1:first}(${2:second}) {${3:third}}" + ], "description": "Creates a setter property inside a class", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "forEach": { "key": "forEach", "prefix": "fre", - "body": ["${1:first}.forEach(${2:second} => {${3:third}})"], + "body": [ + "${1:first}.forEach(${2:second} => {${3:third}})" + ], "description": "Creates a forEach statement", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "forOf": { "key": "forOf", "prefix": "fof", - "body": ["for(let ${1:first} of ${2:second}) {${3:third}}"], + "body": [ + "for(let ${1:first} of ${2:second}) {${3:third}}" + ], "description": "Iterating over property names of iterable objects", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "forIn": { "key": "forIn", "prefix": "fin", - "body": ["for(let ${1:first} in ${2:second}) {${3:third}}"], + "body": [ + "for(let ${1:first} in ${2:second}) {${3:third}}" + ], "description": "Iterating over property values of iterable objects", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "anonymousFunction": { "key": "anonymousFunction", "prefix": "anfn", - "body": ["(${1:first}) => { ${2:second} }"], + "body": [ + "(${1:first}) => { ${2:second} }" + ], "description": "Creates an anonymous function", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "namedFunction": { "key": "namedFunction", "prefix": "nfn", - "body": ["const ${1:first} = (${2:second}) => { ${3:third} }"], + "body": [ + "const ${1:first} = (${2:second}) => { ${3:third} }" + ], "description": "Creates a named function", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "destructingObject": { "key": "destructingObject", "prefix": "dob", - "body": ["const {${2:second}} = ${1:first}"], + "body": [ + "const {${2:second}} = ${1:first}" + ], "description": "Creates and assigns a local variable using object destructing", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "destructingArray": { "key": "destructingArray", "prefix": "dar", - "body": ["const [${2:second}] = ${1:first}"], + "body": [ + "const [${2:second}] = ${1:first}" + ], "description": "Creates and assigns a local variable using array destructing", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "setInterval": { "key": "setInterval", "prefix": "sti", - "body": ["setInterval(() => { ${1:first} }, ${2:second})"], + "body": [ + "setInterval(() => { ${1:first} }, ${2:second})" + ], "description": "Executes the given function at specified intervals", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "setTimeOut": { "key": "setTimeOut", "prefix": "sto", - "body": ["setTimeout(() => { ${1:first} }, ${2:second})"], + "body": [ + "setTimeout(() => { ${1:first} }, ${2:second})" + ], "description": "Executes the given function after the specified delay", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "promise": { "key": "promise", "prefix": "prom", - "body": ["return new Promise((resolve, reject) => { ${1:first} })"], + "body": [ + "return new Promise((resolve, reject) => { ${1:first} })" + ], "description": "Creates and returns a new Promise in the standard ES7 syntax", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "destructProps": { "key": "destructProps", "prefix": "cp", - "body": ["const { ${1:first} } = this.props"], + "body": [ + "const { ${1:first} } = this.props" + ], "description": "Creates and assigns a local variable using props destructing", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "destructState": { "key": "destructState", "prefix": "cs", - "body": ["const { ${1:first} } = this.state"], + "body": [ + "const { ${1:first} } = this.state" + ], "description": "Creates and assigns a local variable using state destructing", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, @@ -1753,42 +2040,54 @@ "emptyState": { "key": "emptyState", "prefix": "est", - "body": ["state = { ${1:first} }"], + "body": [ + "state = { ${1:first} }" + ], "description": "Creates empty state object. To be used in a constructor.", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "componentDidMount": { "key": "componentDidMount", "prefix": "cdm", - "body": ["componentDidMount() { ${1:first} }"], + "body": [ + "componentDidMount() { ${1:first} }" + ], "description": "Invoked once, only on the client (not on the server), immediately after the initial rendering occurs.", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "shouldComponentUpdate": { "key": "shouldComponentUpdate", "prefix": "scu", - "body": ["shouldComponentUpdate(nextProps, nextState) { ${1:first} }"], + "body": [ + "shouldComponentUpdate(nextProps, nextState) { ${1:first} }" + ], "description": "Invoked before rendering when new props or state are being received. ", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "componentDidUpdate": { "key": "componentDidUpdate", "prefix": "cdup", - "body": ["componentDidUpdate(prevProps, prevState) { ${1:first}} "], + "body": [ + "componentDidUpdate(prevProps, prevState) { ${1:first}} " + ], "description": "Invoked immediately after the component's updates are flushed to the DOM.", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "componentWillUnmount": { "key": "componentWillUnmount", "prefix": "cwun", - "body": ["componentWillUnmount() {${1:first} }"], + "body": [ + "componentWillUnmount() {${1:first} }" + ], "description": "Invoked immediately before a component is unmounted from the DOM.", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "getDerivedStateFromProps": { "key": "getDerivedStateFromProps", "prefix": "gdsfp", - "body": ["static getDerivedStateFromProps(props, state) {${1:first}}"], + "body": [ + "static getDerivedStateFromProps(props, state) {${1:first}}" + ], "description": "Invoked right before calling the render method, both on the initial mount and on subsequent updates.", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, @@ -1804,79 +2103,89 @@ "createContext": { "key": "createContext", "prefix": "rcontext", - "body": ["const ${1:first} = React.createContext()"], + "body": [ + "const ${1:first} = React.createContext()" + ], "description": "Create React context", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "createRef": { "key": "createRef", "prefix": "cref", - "body": ["this.${1:first}Ref = React.createRef()"], + "body": [ + "this.${1:first}Ref = React.createRef()" + ], "description": "Create ref statement used inside constructor", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "componentSetStateObject": { "key": "componentSetStateObject", "prefix": "sst", - "body": ["this.setState({${1:first}})"], + "body": [ + "this.setState({${1:first}})" + ], "description": "Performs a shallow merge of nextState into current state", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "componentSetStateFunc": { "key": "componentSetStateFunc", "prefix": "ssf", - "body": ["this.setState((state, props) => { return { ${1:first} }})"], + "body": [ + "this.setState((state, props) => { return { ${1:first} }})" + ], "description": "Performs a shallow merge of nextState into current state", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "componentProps": { "key": "componentProps", "prefix": "props", - "body": ["this.props.${1:first}"], + "body": [ + "this.props.${1:first}" + ], "description": "Access component's props", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "componentState": { "key": "componentState", "prefix": "state", - "body": ["this.state.${1:first}"], + "body": [ + "this.state.${1:first}" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "bindThis": { "key": "bindThis", "prefix": "bnd", - "body": ["this.${1:first} = this.${1:first}.bind(this)"], + "body": [ + "this.${1:first} = this.${1:first}.bind(this)" + ], "description": "Binds this to a method", "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "commentBigBlock": { "key": "commentBigBlock", "prefix": "cmmb", - "body": ["/**", " * ${1:first}", " */"], + "body": [ + "/**", + " * ${1:first}", + " */" + ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "hocComponentWithRedux": { "key": "hocComponentWithRedux", "prefix": "hocredux", "body": [ - "import React from 'react'", "import { connect } from 'react-redux'", "import PropTypes from 'prop-types'", - "", "export const mapStateToProps = state => ({})", - "", "export const mapDispatchToProps = {}", - "", "export const ${1:first} = (WrappedComponent) => {", " const hocComponent = ({ ...props }) => ", - "", " hocComponent.propTypes = {}", - "", " return hocComponent", "}", - "", - "export default WrapperComponent => connect(mapStateToProps, mapDispatchToProps)(${1:first}(WrapperComponent))", - "" + "export default WrapperComponent => connect(mapStateToProps, mapDispatchToProps)(${1:first}(WrapperComponent))" ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, @@ -1884,24 +2193,98 @@ "key": "hocComponent", "prefix": "hoc", "body": [ - "import React from 'react'", "import PropTypes from 'prop-types'", - "", "export default (WrappedComponent) => {", " const hocComponent = ({ ...props }) => ", - "", " hocComponent.propTypes = {}", - "", " return hocComponent", - "}", - "" + "}" ], "scope": "typescript,typescriptreact,javascript,javascriptreact" }, "typeofSnippet": { "key": "typeofSnippet", "prefix": "tpf", - "body": ["typeof ${1:first}"], + "body": [ + "typeof ${1:first}" + ], + "scope": "typescript,typescriptreact,javascript,javascriptreact" + }, + "createBrowserRouterSetup": { + "key": "createBrowserRouterSetup", + "prefix": "rtrsetup", + "body": [ + "import { createBrowserRouter, RouterProvider } from 'react-router-dom'", + "", + "const router = createBrowserRouter([", + " {", + " path: '/',", + " element: <${1:first} />,", + " children: [", + " {", + " path: '${2:second}',", + " element: <${3:third} />,", + " },", + " ],", + " },", + "])", + "", + "function App() {", + " return ", + "}", + "", + "export default App" + ], + "description": "React Router v6 createBrowserRouter setup", + "scope": "typescript,typescriptreact,javascript,javascriptreact" + }, + "routeWithLoaderAction": { + "key": "routeWithLoaderAction", + "prefix": "rtrla", + "body": [ + "import { useLoaderData } from 'react-router-dom'", + "", + "async function loader({ request, params }) {", + " ${1:first}", + "}", + "", + "async function action({ request, params }) {", + " const formData = await request.formData()", + " ${2:second}", + "}", + "", + "function ${1:${TM_FILENAME_BASE}}() {", + " const data = useLoaderData()", + "", + " return (", + " <>${3:third}", + " )", + "}", + "", + "export { loader, action }", + "export default ${1:${TM_FILENAME_BASE}}" + ], + "description": "React Router v6 route with loader and action", + "scope": "typescript,typescriptreact,javascript,javascriptreact" + }, + "useClient": { + "key": "useClient", + "prefix": "usc", + "body": [ + "'use client'", + "" + ], + "description": "React Client Component directive", + "scope": "typescript,typescriptreact,javascript,javascriptreact" + }, + "useServer": { + "key": "useServer", + "prefix": "uss", + "body": [ + "'use server'", + "" + ], + "description": "React Server Action directive", "scope": "typescript,typescriptreact,javascript,javascriptreact" } -} +} \ No newline at end of file diff --git a/src/sourceSnippets/components.ts b/src/sourceSnippets/components.ts index 09ef578..62a44dd 100644 --- a/src/sourceSnippets/components.ts +++ b/src/sourceSnippets/components.ts @@ -30,7 +30,6 @@ type ComponentMappings = { reactFunctionMemoComponent: 'rmc'; reactFunctionMemoComponentWithPropTypes: 'rmcp'; reactFunctionalComponentRedux: 'rfcredux'; - reactFunctionalComponentReduxPropTypes: 'rfcreduxp'; reactFunctionalComponent: 'rfc'; reactFunctionalComponentWithPropTypes: 'rfcp'; reactFunctionalExportComponent: 'rfce'; @@ -322,26 +321,6 @@ const reactFunctionalComponentRedux: ComponentsSnippet = { 'Creates a React functional component with connected redux and ES7 module system', }; -const reactFunctionalComponentReduxPropTypes: ComponentsSnippet = { - key: 'reactFunctionalComponentReduxPropTypes', - prefix: 'rfcreduxp', - body: [ - "import PropTypes from 'prop-types'", - ...reactWithReduxConnect, - '', - `export const ${Placeholders.FileName} = (props) => {`, - ...innerComponent, - '}', - '', - `${Placeholders.FileName}.propTypes = {`, - ` ${Placeholders.SecondTab}: PropTypes.${Placeholders.ThirdTab}`, - '}', - ...reduxComponentExport, - ], - description: - 'DEPRECATED: Creates a React functional component with PropTypes with connected redux and ES7 module system', -}; - export default [ reactArrowFunctionComponent, reactArrowFunctionComponentWithPropTypes, @@ -359,7 +338,6 @@ export default [ reactFunctionMemoComponentWithPropTypes, reactFunctionalComponent, reactFunctionalComponentRedux, - reactFunctionalComponentReduxPropTypes, reactFunctionalComponentWithPropTypes, reactFunctionalExportComponent, ]; diff --git a/src/sourceSnippets/hooks.ts b/src/sourceSnippets/hooks.ts index d8a0377..8425070 100644 --- a/src/sourceSnippets/hooks.ts +++ b/src/sourceSnippets/hooks.ts @@ -1,15 +1,22 @@ import { Placeholders, SnippetMapping } from '../types'; type HookMappings = { - useState: 'useStateSnippet'; + use: 'useSnippet'; + useActionState: 'useActionStateSnippet'; useCallback: 'useCallbackSnippet'; useContext: 'useContextSnippet'; + useDeferredValue: 'useDeferredValueSnippet'; useEffect: 'useEffectSnippet'; + useFormStatus: 'useFormStatusSnippet'; + useId: 'useIdSnippet'; useImperativeHandle: 'useImperativeHandleSnippet'; useLayoutEffect: 'useLayoutEffectSnippet'; useMemo: 'useMemoSnippet'; + useOptimistic: 'useOptimisticSnippet'; useReducer: 'useReducerSnippet'; useRef: 'useRefSnippet'; + useState: 'useStateSnippet'; + useTransition: 'useTransitionSnippet'; }; export type HooksSnippet = SnippetMapping; @@ -109,14 +116,79 @@ const useLayoutEffect: HooksSnippet = { ], }; +const useId: HooksSnippet = { + key: 'useId', + prefix: 'useIdSnippet', + body: [`const ${Placeholders.FirstTab} = useId()`], +}; + +const useTransition: HooksSnippet = { + key: 'useTransition', + prefix: 'useTransitionSnippet', + body: ['const [isPending, startTransition] = useTransition()'], +}; + +const useDeferredValue: HooksSnippet = { + key: 'useDeferredValue', + prefix: 'useDeferredValueSnippet', + body: [ + `const ${Placeholders.FirstTab} = useDeferredValue(${Placeholders.SecondTab})`, + ], +}; + +const useActionState: HooksSnippet = { + key: 'useActionState', + prefix: 'useActionStateSnippet', + body: [ + `const [state, submitAction, isPending] = useActionState(`, + ' async (previousState, formData) => {', + ` ${Placeholders.FirstTab}`, + ' },', + ` ${Placeholders.SecondTab},`, + ')', + ], +}; + +const useFormStatus: HooksSnippet = { + key: 'useFormStatus', + prefix: 'useFormStatusSnippet', + body: ['const { pending, data, method, action } = useFormStatus()'], + description: 'useFormStatus (import from react-dom)', +}; + +const useOptimistic: HooksSnippet = { + key: 'useOptimistic', + prefix: 'useOptimisticSnippet', + body: [ + `const [optimistic${Placeholders.Capitalize}, addOptimistic] = useOptimistic(`, + ` ${Placeholders.FirstTab},`, + ` (state, newValue) => [...state, newValue],`, + ')', + ], +}; + +const useHook: HooksSnippet = { + key: 'use', + prefix: 'useSnippet', + body: [`const ${Placeholders.FirstTab} = use(${Placeholders.SecondTab})`], + description: 'Read a Promise or Context in render (React 19)', +}; + export default [ + useHook, + useActionState, useCallback, useContext, + useDeferredValue, useEffect, + useFormStatus, + useId, useImperativeHandle, useLayoutEffect, useMemo, + useOptimistic, useReducer, useRef, useState, + useTransition, ]; diff --git a/src/sourceSnippets/imports.ts b/src/sourceSnippets/imports.ts index f28281b..120596b 100644 --- a/src/sourceSnippets/imports.ts +++ b/src/sourceSnippets/imports.ts @@ -22,8 +22,14 @@ type ImportsMappings = { importReduxConnect: 'redux'; importRouterLink: 'imbrl'; importRouterNavLink: 'imbrnl'; + importRouterRoutes: 'imrrs'; importRouterSetup: 'imbrc'; - importRouterSwitch: 'imbrs'; + importCreateBrowserRouter: 'imcbr'; + importUseLoaderData: 'imld'; + importUseFetcher: 'imfet'; + importUseNavigate: 'imnav'; + importUseParams: 'impar'; + importUseSearchParams: 'imsp'; }; export type ImportsSnippet = SnippetMapping; @@ -115,13 +121,51 @@ const importBrowserRouterWithRouteAndNavLink: ImportsSnippet = { const importRouterSetup: ImportsSnippet = { key: 'importRouterSetup', prefix: 'imbrc', - body: ["import { Route, Switch, NavLink, Link } from 'react-router-dom'"], + body: ["import { Routes, Route, NavLink, Link } from 'react-router-dom'"], }; -const importRouterSwitch: ImportsSnippet = { - key: 'importRouterSwitch', - prefix: 'imbrs', - body: ["import { Switch } from 'react-router-dom'"], +const importRouterRoutes: ImportsSnippet = { + key: 'importRouterRoutes', + prefix: 'imrrs', + body: ["import { Routes, Route } from 'react-router-dom'"], +}; + +const importUseNavigate: ImportsSnippet = { + key: 'importUseNavigate', + prefix: 'imnav', + body: ["import { useNavigate } from 'react-router-dom'"], +}; + +const importUseParams: ImportsSnippet = { + key: 'importUseParams', + prefix: 'impar', + body: ["import { useParams } from 'react-router-dom'"], +}; + +const importUseSearchParams: ImportsSnippet = { + key: 'importUseSearchParams', + prefix: 'imsp', + body: ["import { useSearchParams } from 'react-router-dom'"], +}; + +const importCreateBrowserRouter: ImportsSnippet = { + key: 'importCreateBrowserRouter', + prefix: 'imcbr', + body: [ + "import { createBrowserRouter, RouterProvider } from 'react-router-dom'", + ], +}; + +const importUseLoaderData: ImportsSnippet = { + key: 'importUseLoaderData', + prefix: 'imld', + body: ["import { useLoaderData } from 'react-router-dom'"], +}; + +const importUseFetcher: ImportsSnippet = { + key: 'importUseFetcher', + prefix: 'imfet', + body: ["import { useFetcher } from 'react-router-dom'"], }; const importRouterLink: ImportsSnippet = { @@ -192,9 +236,15 @@ export default [ importReactWithMemoAndPropTypes, importReactWithPureComponent, importReactWithPureComponentAndPropTypes, + importCreateBrowserRouter, importRouterLink, importRouterNavLink, + importRouterRoutes, importRouterSetup, - importRouterSwitch, importSnippet, + importUseFetcher, + importUseLoaderData, + importUseNavigate, + importUseParams, + importUseSearchParams, ]; diff --git a/src/sourceSnippets/others.ts b/src/sourceSnippets/others.ts index 76bf1db..30cd186 100644 --- a/src/sourceSnippets/others.ts +++ b/src/sourceSnippets/others.ts @@ -41,6 +41,10 @@ type OthersMapping = { setTimeOut: 'sto'; shouldComponentUpdate: 'scu'; typeofSnippet: 'tpf'; + createBrowserRouterSetup: 'rtrsetup'; + routeWithLoaderAction: 'rtrla'; + useClient: 'usc'; + useServer: 'uss'; }; export type OthersSnippet = SnippetMapping; @@ -407,6 +411,77 @@ const typeofSnippet: OthersSnippet = { body: [`typeof ${Placeholders.FirstTab}`], }; +const createBrowserRouterSetup: OthersSnippet = { + key: 'createBrowserRouterSetup', + prefix: 'rtrsetup', + body: [ + "import { createBrowserRouter, RouterProvider } from 'react-router-dom'", + '', + 'const router = createBrowserRouter([', + ' {', + ` path: '/',`, + ` element: <${Placeholders.FirstTab} />,`, + ' children: [', + ' {', + ` path: '${Placeholders.SecondTab}',`, + ` element: <${Placeholders.ThirdTab} />,`, + ' },', + ' ],', + ' },', + '])', + '', + 'function App() {', + ' return ', + '}', + '', + 'export default App', + ], + description: 'React Router v6 createBrowserRouter setup', +}; + +const routeWithLoaderAction: OthersSnippet = { + key: 'routeWithLoaderAction', + prefix: 'rtrla', + body: [ + "import { useLoaderData } from 'react-router-dom'", + '', + `async function loader({ request, params }) {`, + ` ${Placeholders.FirstTab}`, + '}', + '', + `async function action({ request, params }) {`, + ' const formData = await request.formData()', + ` ${Placeholders.SecondTab}`, + '}', + '', + `function ${Placeholders.FileName}() {`, + ' const data = useLoaderData()', + '', + ' return (', + ` <>${Placeholders.ThirdTab}`, + ' )', + '}', + '', + `export { loader, action }`, + `export default ${Placeholders.FileName}`, + ], + description: 'React Router v6 route with loader and action', +}; + +const useClient: OthersSnippet = { + key: 'useClient', + prefix: 'usc', + body: ["'use client'", ''], + description: 'React Client Component directive', +}; + +const useServer: OthersSnippet = { + key: 'useServer', + prefix: 'uss', + body: ["'use server'", ''], + description: 'React Server Action directive', +}; + export default [ exportDefault, exportDestructing, @@ -448,4 +523,8 @@ export default [ hocComponentWithRedux, hocComponent, typeofSnippet, + createBrowserRouterSetup, + routeWithLoaderAction, + useClient, + useServer, ]; diff --git a/src/sourceSnippets/redux.ts b/src/sourceSnippets/redux.ts index 0315fc9..a2f9247 100644 --- a/src/sourceSnippets/redux.ts +++ b/src/sourceSnippets/redux.ts @@ -3,10 +3,13 @@ import { Placeholders, SnippetMapping } from '../types'; type ReduxMapping = { importReduxConnect: 'redux'; reduxAction: 'rxaction'; + reduxApi: 'rxapi'; + reduxAsyncThunk: 'rxthunk'; reduxConst: 'rxconst'; reduxReducer: 'rxreducer'; reduxSelector: 'rxselect'; reduxSlice: 'rxslice'; + reduxSliceWithExtraReducers: 'rxslicex'; mappingToProps: 'reduxmap'; }; @@ -88,6 +91,73 @@ const reduxSlice: ReduxSnippet = { ], }; +const reduxSliceWithExtraReducers: ReduxSnippet = { + key: 'reduxSliceWithExtraReducers', + prefix: 'rxslicex', + body: [ + "import { createSlice } from '@reduxjs/toolkit'", + '', + 'const initialState = {', + ` ${Placeholders.FirstTab}`, + ' status: \'idle\',', + '}', + '', + `const ${Placeholders.FileName} = createSlice({`, + ` name: '${Placeholders.SecondTab}',`, + ' initialState,', + ' reducers: {},', + ' extraReducers: (builder) => {', + ' builder', + ` .addCase(${Placeholders.ThirdTab}.pending, (state) => {`, + " state.status = 'loading'", + ' })', + ` .addCase(${Placeholders.ThirdTab}.fulfilled, (state, action) => {`, + " state.status = 'succeeded'", + ' })', + ` .addCase(${Placeholders.ThirdTab}.rejected, (state) => {`, + " state.status = 'failed'", + ' })', + ' },', + '})', + '', + `export const {} = ${Placeholders.FileName}.actions`, + '', + `export default ${Placeholders.FileName}.reducer`, + ], +}; + +const reduxAsyncThunk: ReduxSnippet = { + key: 'reduxAsyncThunk', + prefix: 'rxthunk', + body: [ + "import { createAsyncThunk } from '@reduxjs/toolkit'", + '', + `export const ${Placeholders.FirstTab} = createAsyncThunk('${Placeholders.SecondTab}', async () => {`, + ` ${Placeholders.ThirdTab}`, + '})', + ], +}; + +const reduxApi: ReduxSnippet = { + key: 'reduxApi', + prefix: 'rxapi', + body: [ + "import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'", + '', + `export const ${Placeholders.FirstTab}Api = createApi({`, + ` reducerPath: '${Placeholders.FirstTab}Api',`, + ` baseQuery: fetchBaseQuery({ baseUrl: '${Placeholders.SecondTab}' }),`, + ' endpoints: (builder) => ({', + ` ${Placeholders.ThirdTab}: builder.query({`, + " query: () => '/',", + ' }),', + ' }),', + '})', + '', + `export const { } = ${Placeholders.FirstTab}Api`, + ], +}; + const mappingToProps: ReduxSnippet = { key: 'mappingToProps', prefix: 'reduxmap', @@ -101,9 +171,12 @@ const mappingToProps: ReduxSnippet = { export default [ importReduxConnect, reduxAction, + reduxApi, + reduxAsyncThunk, reduxConst, reduxReducer, reduxSelector, reduxSlice, + reduxSliceWithExtraReducers, mappingToProps, ]; diff --git a/src/sourceSnippets/sharedSnippets.ts b/src/sourceSnippets/sharedSnippets.ts index 6a199ac..3cb5a4a 100644 --- a/src/sourceSnippets/sharedSnippets.ts +++ b/src/sourceSnippets/sharedSnippets.ts @@ -33,14 +33,14 @@ export const reduxComponentExport = [ export const innerComponent = [ ' return (', - `
${Placeholders.FirstTab}
`, + ` <>${Placeholders.FirstTab}`, ' )', ]; export const innerComponentReturn = [ ' render() {', ' return (', - `
${Placeholders.FirstTab}
`, + ` <>${Placeholders.FirstTab}`, ' )', ' }', ]; diff --git a/src/sourceSnippets/typescript.ts b/src/sourceSnippets/typescript.ts index d024a0c..5cc9c1d 100644 --- a/src/sourceSnippets/typescript.ts +++ b/src/sourceSnippets/typescript.ts @@ -133,7 +133,7 @@ const typescriptReactArrowFunctionComponent: TypescriptSnippet = { ...react, '', ...propsTypeInterface, - `const ${Placeholders.FileName} = (props: Props) => {`, + `export const ${Placeholders.FileName} = (props: Props) => {`, ...innerComponent, '}', ],