From d911f3e30894e54401dde49d0bb7a8486e050ccf Mon Sep 17 00:00:00 2001 From: Amedee d'Aboville Date: Wed, 22 Nov 2023 18:39:15 +0100 Subject: [PATCH 1/2] WIP: react-update-imports pattern --- .grit/patterns/react_update_imports.md | 327 +++++++++++++++++++++++++ 1 file changed, 327 insertions(+) create mode 100644 .grit/patterns/react_update_imports.md diff --git a/.grit/patterns/react_update_imports.md b/.grit/patterns/react_update_imports.md new file mode 100644 index 00000000..d11c156e --- /dev/null +++ b/.grit/patterns/react_update_imports.md @@ -0,0 +1,327 @@ +# Update React imports + +This pattern: + +- Removes imports of React that are unneeded +- Converts default imports if possible: +- If there are named imports, converts `import React from 'react'` into named imports `import { useState } from 'react'` +- If there are no named imports, but the `React` variable is used, converts `import React from 'react'` into `import * as React from "react"` + +```grit +engine marzano(0.1) +language js +pattern my_remove_import($from) { + $name where { + // Handle named imports + $program <: maybe contains bubble($name, $from) `import $clause from $raw_source` as $import where { + $raw_source <: contains $from, + $clause <: or { + // Handle module import + import_clause(default=$name) where { + $import => . + }, + // Handle named import + import_clause(default=$default_export, name=named_imports($imports)) as $clause where { + $others = `false`, + if ($imports <: [$name]) { + if ($default_export <: .) { + $import => . + } else { + $clause => $default_export + } + } else { + $imports <: some $name => . + } + }, + //Handle namespace import like import * as $name + import_clause(name=namespace_import(namespace=$name)) where { + $import => . + }, + } + } + } +} +//unused +pattern gather_imported_symbols($res) { + `React.$symbol` as $reactSymbol where { + $res <: not some $reactSymbol, + $reactSymbol => $symbol + } +} + + +`import $import_clause from $source` as $import_line where { + $import_clause <: contains `React` as $react where { + $from = `react`, + if (!$program <: contains `React.$_`) { + $react <: my_remove_import($from) + } else { + if(!$import_clause <: contains `* as React`) { + $react => `* as React` + } + } + } +} +//or { +// `React.$symbol` => $symbol, +// import_statement(import=import_clause(name=namespace_import(namespace=`React`))) as $star_import where { +// $star_import => . +// }, +// `React` as $react where { +// $from = `react`, +// $react <: my_remove_import($from) +// }, +// and { +// $neededImports = [], +// } +//} +``` + +## Test case: jsx-element + +```javascript +import * as React from "react"; + +
Hi
; +``` + +```javascript +
Hi
+``` + +## Test case: jsx-fragment + +```javascript +import * as React from "react"; + +<>; +``` + +```javascript +<> +``` + +## Test case: react-not-removed + +```javascript +import React from "react"; + +React.createElement("div", {}); + +Promise.resolve(React); + +
Hi
; +``` + +```javascript +import * as React from "react"; + +React.createElement("div", {}); + +Promise.resolve(React); + +
Hi
; +``` + +## Test case: variable-already-used + +```javascript +import * as React from "react"; + +React.createElement("div", {}); + +createElement("someFunction"); + +
Hi
; +``` + +## Test case: default-and-multiple-specifiers-import-react-variable + +```javascript +import React, { createElement, useState } from "react"; + +React.createElement("div", {}); + +
Hi
; +``` + +```javascript +import { createElement, useState } from "react"; +import * as React from "react"; + +React.createElement("div", {}); + +
Hi
; +``` + +## Test case: default-and-multiple-specifiers-import + +```javascript +import React, { type Element, createElement, useState } from "react"; + +
Hi
; +``` + +```javascript +import type { Element } from "react"; +import { createElement, useState } from "react"; + +
Hi
; +``` + +## Test case: leading-comment + +```javascript +/** + * Hello world. + */ + +import * as React from "react"; + +
; +``` + +```javascript +/** + * Hello world. + */ + +
+``` + +## Test case: react-already-used-named-export + +```javascript +import * as React from "react"; + +React.useState(false); +``` + +## Test case: react-basic-default-export-jsx-element-react-variable + +```javascript +import React from "react"; + +React.createElement("div", {}); + +
; +``` + +```javascript +import { createElement } from "react"; + +createElement("div", {}); + +
; +``` + +## Test case: react-basic-default-export-jsx-element + +```javascript +import React from "react"; + +
; +``` + +```javascript +
+``` + +## Test case: react-basic-default-export + +```javascript +import React from "react"; + +React.createElement("div", "la"); +``` + +```javascript +import { createElement } from "react"; + +createElement("div", "la"); +``` + +## Test case: react-jsx-member-expression + +```javascript +import React, { useState } from "react"; + +; +``` + +```javascript +import { Fragment, useState } from "react"; + +; +``` + +## Test case: react-type-default-export + +```javascript +import type React from "react"; +import * as React from "react"; + +
Hi
; +``` + +```javascript +import type React from "react"; + +
Hi
; +``` + +## Test case: react-type-not-removed + +```javascript +import type React, { Node } from "react"; +import * as React from "react"; + +
Hi
; +``` + +```javascript +import type React, { Node } from "react"; + +
Hi
; +``` + +## Test case: destructure-named-imports-react-not-removed + +```javascript +import * as React from "react"; + +React.createElement("div", {}); + +Promise.resolve(React); + +
Hi
; +``` + +## Test case: destructure-named-imports-variable-used + +```javascript +import * as React from "react"; +import { createElement } from "react"; + +React.createElement("div", {}); + +
Hi
; +``` + +## Test case: destructure-named-imports + +```javascript +import * as React from "react"; + +React.useState(false); + +
; +``` + +```javascript +import { useState } from "react"; + +useState(false); + +
; +``` From 06abe5a4866d706428337e076118018e8439a7fd Mon Sep 17 00:00:00 2001 From: Amedee d'Aboville Date: Wed, 22 Nov 2023 18:51:05 +0100 Subject: [PATCH 2/2] Committed the wrong version of the file --- .grit/patterns/react_update_imports.md | 27 ++++++++++++-------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/.grit/patterns/react_update_imports.md b/.grit/patterns/react_update_imports.md index d11c156e..4abf62d2 100644 --- a/.grit/patterns/react_update_imports.md +++ b/.grit/patterns/react_update_imports.md @@ -7,9 +7,19 @@ This pattern: - If there are named imports, converts `import React from 'react'` into named imports `import { useState } from 'react'` - If there are no named imports, but the `React` variable is used, converts `import React from 'react'` into `import * as React from "react"` +## Existing issues + +This pattern does not do: + +- collect the used React symbols to change `import React from 'react'` into `import { useState, useMemo} from 'react` +- figure out when the `React` symbol is properly used and should _not_ be removed, the `react-not-removed` test case +- handle `import type React` separately + ```grit engine marzano(0.1) language js + +//patch over bugs in remove_import, needed until this is upstreamed pattern my_remove_import($from) { $name where { // Handle named imports @@ -44,8 +54,8 @@ pattern my_remove_import($from) { //unused pattern gather_imported_symbols($res) { `React.$symbol` as $reactSymbol where { - $res <: not some $reactSymbol, - $reactSymbol => $symbol + $reactSymbols <: not some $reactSymbol, + $reactSymbols += $symbol } } @@ -62,19 +72,6 @@ pattern gather_imported_symbols($res) { } } } -//or { -// `React.$symbol` => $symbol, -// import_statement(import=import_clause(name=namespace_import(namespace=`React`))) as $star_import where { -// $star_import => . -// }, -// `React` as $react where { -// $from = `react`, -// $react <: my_remove_import($from) -// }, -// and { -// $neededImports = [], -// } -//} ``` ## Test case: jsx-element